[
  {
    "path": ".gitattributes",
    "content": "gradle.properties merge=ours\nsrc/main/resources/META-INF/mods.toml merge=ours\nsrc/main/resources/META-INF/neoforge.mods.toml merge=ours\nCHANGELOG.md merge=ours\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "**:tada: Thanks you for taking the time to contribute to this repository! :tada:**\n\nIssues can be created for bugs :bug:, feature suggestions :heavy_plus_sign:, performance issues :snail:, questions :question: or anything else.\nAll issues should follow the provided template when creating a new issue.\n\nBefore opening new issues, make sure that the issue does not already exist, so search through all issues (even the closed ones) before opening a new issue.\n\n### Performance issues :snail:\n\nIf you encounter a performance issue (i.e. server/client lag), make sure to read the following.\n\nYou can use the [Sampler mod](https://forum.industrial-craft.net/thread/10820) for _profiling_ your game.\nThis mod can create `.nps` files, and can help us to see what parts of the game cause performance issues using software such as [VisualVM](https://visualvm.github.io/).\nWhen opening a performance issue, make sure to send us this `.nps` file.\n\nIf you are encountering client lag (FPS issues), run this mod in your client.\nIf you are encountering server lag (TPS issues), run this mod on your server.\n\nWhen the lag is starting, run `/sampler start` to start profiling, and `/sampler stop` to stop.\nRun `/sampler export [your file name]` to export this profiling result to an `.nps` file.\n\nDownload Sampler for Minecraft 1.12: http://files.player.to/sampler-1.73.jar\n\n### Pull Request\n\nPull requests are more than welcome! Before submitting one, make sure to discuss your plans with the maintainers via an issue.\nWhen you submit PRs, you have to sign the [Cyclops Contributor License Agreement](https://cla-assistant.io/CyclopsMC/CyclopsCore),\nwhich makes sure that you give us the appropriate permissions to use your submission, and that your submission is not owned by someone else.\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "patreon: kroeser\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.md",
    "content": "---\r\nname: \"🐛 Bug report\"\r\nabout: If something is not working as expected or crashes\r\n\r\n---\r\n\r\n<!--Thanks in advance for this issue, you're awesome!-->\r\n<!--Before you read any further, can you double-check to make sure you're using the latest versions of all related mods?-->\r\n<!--Please fill in the following template and make sure your title clear and concisely summarizes the issue.-->\r\n\r\n#### Issue type:\r\n\r\n- :bug: Bug <!--Don't change this issue type!-->\r\n\r\n____\r\n\r\n#### Short description:\r\n\r\n<!--A clear and concise description of what the bug is.-->\r\n\r\n#### Steps to reproduce the problem:\r\n\r\n<!--Make sure that the steps are simple, and can be easily and fully followed by other people-->\r\n\r\n1. ...\r\n\r\n#### Expected behaviour:\r\n\r\n<!--A clear and concise description of what you expected to happen.-->\r\n\r\n____\r\n\r\n#### Versions:\r\n\r\n<!--Exact versions of the following mods, not just *latest*.-->\r\n\r\n- This mod: \r\n- Minecraft: \r\n- Mod loader version: <!-- For example, \"NeoForge 21.1.18\", \"Forge 50.1.12\", or \"Fabric 0.102.1\" -->\r\n\r\n<!--WHEN USING MODPACKS: First report this bug to the modpack author! As it could be a configuration or mod conflict issue.-->\r\n<!--Before reporting, make sure to try reproducing without any other mods installed.-->\r\n<!--If this problem only occurs in combination with other mods, attempt to find the smallest set of mods that causes the problem.-->\r\n\r\n#### Log file:\r\n\r\n<!--Always required for crashes. For other bugs, we might ask for it.-->\r\n<!--Don't paste contents here directly, but use something like http://pastebin.com/-->\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.md",
    "content": "---\r\nname: \"➕ Feature request\"\r\nabout: Suggest an idea for this project\r\n\r\n---\r\n\r\n<!--Thanks in advance for this issue, you're awesome!-->\r\n<!--Please fill in the following template and make sure your title clear and concisely summarizes the issue.-->\r\n\r\n#### Issue type:\r\n\r\n- :heavy_plus_sign: Feature request <!--Don't change this issue type!-->\r\n\r\n____\r\n\r\n#### Short description:\r\n\r\n<!--A clear and concise description of what you want to happen.-->\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Performance_issue.md",
    "content": "---\r\nname: \"🐌 Performance issue\"\r\nabout: An issue with the performance of this mod, such as abnormal lag.\r\n\r\n---\r\n\r\n<!--Thanks in advance for this issue, you're awesome!-->\r\n<!--Before you read any further, can you double-check to make sure you're using the latest versions of all related mods?-->\r\n<!--Please fill in the following template and make sure your title clear and concisely summarizes the issue.-->\r\n\r\n#### Issue type:\r\n\r\n- :snail: Performance issue <!--Don't change this issue type!-->\r\n\r\n____\r\n\r\n#### Short description:\r\n\r\n<!--A clear and concise description of what the issue is.-->\r\n\r\n#### Steps to reproduce the problem:\r\n\r\n<!--Make sure that the steps are simple, and can be easily and fully followed by other people-->\r\n\r\n1. ...\r\n\r\n____\r\n\r\n#### Versions:\r\n\r\n<!--Exact versions of the following mods, not just *latest*.-->\r\n\r\n- This mod: \r\n- Minecraft: \r\n- Forge: \r\n\r\n#### Profiler output:\r\n\r\n<!--Information on how to profile your game: https://github.com/CyclopsMC/CyclopsCore/blob/master-1.18/.github/CONTRIBUTING.md#performance-issues-snail-->\r\n\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Question.md",
    "content": "---\r\nname: \"❓ Question\"\r\nabout: A general question\r\n\r\n---\r\n\r\n<!--Thanks in advance for this issue, you're awesome!-->\r\n<!--Please fill in the following template and make sure your title clear and concisely summarizes the issue.-->\r\n\r\n#### Issue type:\r\n\r\n- :question: Question <!--Don't change this issue type!-->\r\n\r\n____\r\n\r\n#### Question:\r\n\r\n<!--A clear and concisely formulated question.-->\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--Thanks in advance for this issue, you're awesome! Please fill in the following template and make sure your title clear and concisely summarizes the issue.-->\n\n<!---\nThanks for filing an issue! Before you submit, please read the following:\n\nCheck the other issue templates if you are trying to submit a bug report, feature request, performance issue, or question\nSearch open/closed issues before submitting since someone might have asked the same thing before!\n-->\n"
  },
  {
    "path": ".github/issue-commenter.yml",
    "content": "labels:\n  - name: bug\n    labeled:\n      issue:\n        body: Thanks for reporting!\n  - name: feature\n    labeled:\n      issue:\n        body: Thanks for the suggestion!\n  - name: feature-long-term\n    labeled:\n      issue:\n        body: Thanks for the suggestion!\n  - name: performance\n    labeled:\n      issue:\n        body: Thanks for reporting!\n  - name: question\n    labeled:\n      issue:\n        body: Someone will answer your question soon. In the meantime, you might be able to get help more quickly on our [Discord server](https://discord.gg/9yDxubB).\n  - name: invalid-template\n    labeled:\n      issue:\n        body: '@{{ issue.user.login }} Your issue appears to be not following one of the allowed issue templates, which breaks our automation tools. Please update your issue to the proper template.'\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "bug:\n  - '- :bug: Bug'\nfeature-long-term:\n  - '- :heavy_plus_sign: Feature request'\nperformance:\n  - '- :snail: Performance issue'\nquestion:\n  - '- :question: Question'\n\nmc-1.12:\n  - 'Minecraft: 1.12'\nmc-1.14:\n  - 'Minecraft: 1.14'\nmc-1.15:\n  - 'Minecraft: 1.15'\nmc-1.16:\n  - 'Minecraft: 1.16'\nmc-1.18:\n  - 'Minecraft: 1.18'\nmc-1.19:\n  - 'Minecraft: 1.19'\nmc-1.20:\n  - 'Minecraft: 1.20'\nmc-1.21:\n  - 'Minecraft: 1.21'\nmc-26:\n  - 'Minecraft: 26'\nmc-27:\n  - 'Minecraft: 27'\nmc-28:\n  - 'Minecraft: 28'\nmc-29:\n  - 'Minecraft: 29'\nmc-30:\n  - 'Minecraft: 30'\n"
  },
  {
    "path": ".github/no-response.yml",
    "content": "# Configuration for probot-no-response - https://probot.github.io/apps/no-response/\n\n# Number of days of inactivity before an Issue is closed for lack of response\ndaysUntilClose: 14\n# Label requiring a response\nresponseRequiredLabel: more-information-needed\n# Comment to post when closing an Issue for lack of response. Set to `false` to disable\ncloseComment: >\n  This issue has been automatically closed because there has been no response\n  to our request for more information from the original author. With only the\n  information that is currently in the issue, we don't have enough information\n  to take action. Please reach out if you have or find the answers we need so\n  that we can investigate further.\n"
  },
  {
    "path": ".github/parse_benchmark_results.sh",
    "content": "#!/bin/bash\n# Parse benchmark results from game tests and convert to JSON format\n# This script reads the benchmark results file and converts it to JSON format\n# for use with benchmark-action/github-action-benchmark\n\nset -e\n\nBENCH_FILE=\"benchmark_results.json\"\nRESULTS_FILE=\"run/logs/benchmark_results.txt\"\n\necho \"Parsing benchmark results...\"\n\n# Initialize the JSON array\necho \"[\" > \"$BENCH_FILE\"\n\n# Default values if file doesn't exist\nif [ ! -f \"$RESULTS_FILE\" ]; then\n    echo \"Benchmark results file not found at $RESULTS_FILE\"\n    exit 1;\nfi\n\nFIRST=true\nwhile IFS= read -r line; do\n    # Skip empty lines\n    if [ -z \"$line\" ]; then\n        continue\n    fi\n\n    # Parse the line: preset=<preset> size=<size> avgNetworkTickTime=<time> avgServerTickTime=<time>\n    # Using sed instead of grep -oP for macOS compatibility\n    preset=$(echo \"$line\" | sed -n 's/.*preset=\\([^ ]*\\).*/\\1/p')\n    size=$(echo \"$line\" | sed -n 's/.*size=\\([0-9]*\\).*/\\1/p')\n    networkTickTime=$(echo \"$line\" | sed -n 's/.*avgNetworkTickTime=\\([0-9.]*\\).*/\\1/p')\n    serverTickTime=$(echo \"$line\" | sed -n 's/.*avgServerTickTime=\\([0-9.]*\\).*/\\1/p')\n\n    if [ -n \"$preset\" ] && [ -n \"$size\" ] && [ -n \"$networkTickTime\" ]; then\n        # Output network tick time metric\n        if [ \"$FIRST\" = true ]; then\n            FIRST=false\n        else\n            echo \",\" >> \"$BENCH_FILE\"\n        fi\n\n        cat >> \"$BENCH_FILE\" << EOF\n  {\n    \"name\": \"NETWORK LOAD: ${preset}_size_${size}\",\n    \"unit\": \"tick time (ms)\",\n    \"value\": $networkTickTime\n  }\nEOF\n    fi\n\n    # Output server tick time metric if available\n    if [ -n \"$serverTickTime\" ]; then\n        echo \",\" >> \"$BENCH_FILE\"\n        cat >> \"$BENCH_FILE\" << EOF\n  {\n    \"name\": \"SERVER LOAD: ${preset}_size_${size}\",\n    \"unit\": \"tick time (ms)\",\n    \"value\": $serverTickTime\n  }\nEOF\n    fi\ndone < \"$RESULTS_FILE\"\n\n# Close the JSON array\necho \"\" >> \"$BENCH_FILE\"\necho \"]\" >> \"$BENCH_FILE\"\n\n# Display the results\necho \"✓ Benchmark results parsed successfully:\"\necho \"\"\ncat \"$BENCH_FILE\"\n\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: \"CI\"\non: [push, pull_request]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n      - name: 'Checkout'\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n      - name: 'Setup Java'\n        uses: actions/setup-java@v4\n        with:\n          distribution: 'microsoft'\n          java-version: 25\n      - name: 'Setup Gradle'\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-version: wrapper\n          cache-read-only: false\n      - name: 'Mark tag as release'\n        if: startsWith(github.ref, 'refs/tags/')\n        run: echo \"RELEASE=true\" >> $GITHUB_ENV\n      - name: 'Unlock keystore'\n        id: unlock_keystore\n        env:\n          KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}\n        if: ${{ env.KEYSTORE_BASE64 }}\n        uses: timheuer/base64-to-file@v1\n        with:\n          fileName: 'keystore.jks'\n          encodedString: ${{ secrets.KEYSTORE_BASE64 }}\n      - name: 'Expose keystore'\n        env:\n          KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}\n        if: ${{ env.KEYSTORE_BASE64 }}\n        run: cp ${{ steps.unlock_keystore.outputs.filePath }} .\n      - name: 'Build'\n        run: ./gradlew build --no-daemon\n        env:\n          SIGN_KEYSTORE: ${{ secrets.SIGN_KEYSTORE }}\n          SIGN_ALIAS: ${{ secrets.SIGN_ALIAS }}\n          SIGN_KEYPASS: ${{ secrets.SIGN_KEYPASS }}\n          SIGN_STOREPASS: ${{ secrets.SIGN_STOREPASS }}\n          MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}\n          MAVEN_KEY: ${{ secrets.MAVEN_KEY }}\n          GITHUB_USER: ${{ github.actor }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: 'Test'\n        run: ./gradlew test runGameTestServer jacocoTestReport\n      - name: 'Submit coverage results'\n        uses: coverallsapp/github-action@v2\n      - name: 'Deploy as GitHub CI artifacts'\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          path: build/libs/*\n      - name: 'Deploy to CurseForge'\n        if: startsWith(github.ref, 'refs/tags/') || (!startsWith(github.event.head_commit.message, 'Bump mod version') && startsWith(github.ref, 'refs/heads/master'))\n        env:\n          CURSEFORGE_KEY_SECRET: ${{ secrets.CURSEFORGE_KEY_SECRET }}\n        run: ./gradlew publishCurseForge\n      - name: 'Deploy to Modrinth'\n        continue-on-error: true\n        if: startsWith(github.ref, 'refs/tags/') || (!startsWith(github.event.head_commit.message, 'Bump mod version') && startsWith(github.ref, 'refs/heads/master'))\n        env:\n          MODRINTH_KEY_SECRET: ${{ secrets.MODRINTH_KEY_SECRET }}\n        run: ./gradlew modrinth\n      - name: 'Deploy to Maven'\n        if: startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/feature')\n        env:\n          MAVEN_URL: ${{ secrets.MAVEN_URL }}\n          MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}\n          MAVEN_KEY: ${{ secrets.MAVEN_KEY }}\n        run: ./gradlew publish\n      - name: 'Deploy JavaDoc to GitHub Pages'\n        if: startsWith(github.ref, 'refs/heads/master')\n        uses: JamesIves/github-pages-deploy-action@v4\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          branch: gh-pages\n          folder: build/docs/javadoc\n          clean: true"
  },
  {
    "path": ".github/workflows/close-issues-no-response.yml",
    "content": "name: \"Close issues that received no response\"\non:\n  schedule:\n  - cron: \"0 0 * * *\"\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/stale@v9\n      with:\n        repo-token: \"${{ secrets.GITHUB_TOKEN }}\"\n        stale-issue-label: \"more-information-needed\"\n        stale-issue-message: 'This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we do not have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further.'\n        days-before-close: 14\n        days-before-stale: -1\n    - uses: actions/stale@v9\n      with:\n        repo-token: \"${{ secrets.GITHUB_TOKEN }}\"\n        only-labels: \"invalid-template\"\n        stale-issue-label: \"invalid-template\"\n        stale-issue-message: 'This issue has been automatically closed because it does not follow the issue template. Feel free to recreate this issue'\n        days-before-close: 7\n        days-before-stale: -1\n"
  },
  {
    "path": ".github/workflows/copilot-setup-steps.yml",
    "content": "name: \"Copilot Setup Steps\"\n\non:\n  workflow_dispatch:\n  push:\n    paths:\n      - .github/workflows/copilot-setup-steps.yml\n  pull_request:\n    paths:\n      - .github/workflows/copilot-setup-steps.yml\n\njobs:\n  # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.\n  copilot-setup-steps:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n\n    # Steps that will run before the agent starts\n    steps:\n      - name: 'Checkout'\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n      - name: Read Java version from gradle.properties\n        id: java_version\n        run: |\n          if [ -f gradle.properties ] && grep -q '^java_version=' gradle.properties; then\n            VERSION=$(grep '^java_version=' gradle.properties | cut -d'=' -f2)\n          else\n            VERSION=17\n          fi\n          echo \"Using Java version: $VERSION\"\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n      - name: 'Setup Java'\n        uses: actions/setup-java@v4\n        with:\n          distribution: 'microsoft'\n          java-version: ${{ steps.java_version.outputs.version }}\n      - name: 'Setup Gradle'\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-version: wrapper\n          cache-read-only: false\n      - name: 'Build'\n        run: ./gradlew build --no-daemon\n        env:\n          MAVEN_USERNAME: ${{ github.actor }}\n          MAVEN_KEY: ${{ secrets.GITHUB_TOKEN }}\n          GITHUB_USER: ${{ github.actor }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: 'Warmup for game test server'\n        run: ./gradlew runGameTestServer --no-daemon\n"
  },
  {
    "path": ".github/workflows/crowdin.yml",
    "content": "name: \"Crowdin Sync Translations\"\n\non:\n  schedule:\n    - cron: '0 7 * * *'\n  workflow_dispatch:\n\njobs:\n  synchronize-with-crowdin:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Crowdin action\n        uses: crowdin/github-action@v2\n        with:\n          upload_sources: true\n          upload_translations: false\n          download_translations: true\n          localization_branch_name: l10n_crowdin_translations\n          create_pull_request: true\n          pull_request_title: 'New Translations'\n          pull_request_body: 'New translations by [Crowdin GitHub Action](https://github.com/crowdin/github-action)'\n          commit_message: 'Add translations through Crowdin'\n        env:\n          GITHUB_TOKEN: ${{ secrets.PAT }}\n          CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} # A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api\n          CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/fuzzing.yml",
    "content": "name: \"Fuzzing\"\non:\n  schedule:\n    - cron: '30 2 * * *'  # Run every day at 2:30 AM UTC\n  workflow_dispatch:  # Allow manual triggering for testing\n  push:\n    paths:\n      - src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsFuzzing.java\n      - src/main/java/org/cyclops/integrateddynamics/gametest/fuzzing/**\n      - .github/workflows/fuzzing.yml\n  pull_request:\n    paths:\n      - src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsFuzzing.java\n      - src/main/java/org/cyclops/integrateddynamics/gametest/fuzzing/**\n      - .github/workflows/fuzzing.yml\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  fuzz:\n    name: Fuzz\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n    steps:\n      - name: 'Checkout'\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n      - name: 'Setup Java'\n        uses: actions/setup-java@v4\n        with:\n          distribution: 'microsoft'\n          java-version: 21\n      - name: 'Setup Gradle'\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-version: wrapper\n          cache-read-only: false\n      - name: 'Build'\n        run: ./gradlew build -x test --no-daemon\n        env:\n          GITHUB_USER: ${{ github.actor }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: 'Run fuzzing game tests'\n        id: fuzzing\n        run: ./gradlew runGameTestServer\n        env:\n          FUZZING_ITERATIONS: '1000'\n      - name: 'Upload fuzzing crash structures'\n        uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: fuzzing-crash-structures.zip\n          path: run/fuzzing_crashes/\n          if-no-files-found: ignore\n      - name: 'Create issue for fuzzing failure'\n        if: failure() && steps.fuzzing.outcome == 'failure'\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const fs = require('fs');\n            const path = require('path');\n            \n            // Get version from gradle.properties\n            const gradleProps = fs.readFileSync('gradle.properties', 'utf8');\n            const versionMatch = gradleProps.match(/mod_version\\s*=\\s*(.+)/);\n            const modVersion = versionMatch ? versionMatch[1].trim() : 'unknown';\n            \n            // Find crash structure files\n            const crashDir = 'runs/gameTestServer/fuzzing_crashes/';\n            let crashFiles = [];\n            if (fs.existsSync(crashDir)) {\n              crashFiles = fs.readdirSync(crashDir).filter(f => f.endsWith('.nbt'));\n            }\n            \n            // Extract [Fuzzing] log lines\n            let fuzzingLogs = [];\n            const logFile = 'runs/gameTestServer/logs/latest.log';\n            if (fs.existsSync(logFile)) {\n              const logContent = fs.readFileSync(logFile, 'utf8');\n              fuzzingLogs = logContent.split('\\n').filter(line => line.includes('[Fuzzing]'));\n            }\n            \n            // Build the issue body\n            let issueBody = `## Fuzzing Test Failure\n\n            A fuzz test has detected a crash or error in the Integrated Dynamics network generation or execution.\n            \n            ### Reproduction Instructions\n            \n            1. Download the structure file(s) from the [workflow artifacts](${context.payload.repository.html_url}/actions/runs/${context.runId})\n            2. In Minecraft, use a structure block to load the .nbt file\n            3. The crash should occur when the network is placed and begins executing\n            \n            ### Crash Structure Files\n            \n            `;\n            \n                        if (crashFiles.length > 0) {\n                          issueBody += `The following structure file(s) were generated during fuzzing:\\n\\n`;\n                          crashFiles.forEach(file => {\n                            issueBody += `- \\`${file}\\`\\n`;\n                          });\n                        } else {\n                          issueBody += `No structure files were captured (the error probably occurred during fuzzed network generation)\\n`;\n                        }\n            \n                        issueBody += `\n            ### Fuzzing Log Output\n            \n            `;\n            \n                        if (fuzzingLogs.length > 0) {\n                          issueBody += `\\`\\`\\`\\n${fuzzingLogs.join('\\n')}\\n\\`\\`\\`\\n`;\n                        } else {\n                          issueBody += `No [Fuzzing] log lines found.\\n`;\n                        }\n            \n                        issueBody += `\n            ### Version Information\n            \n            - **Mod Version**: ${modVersion}\n            - **Minecraft Version**: 1.21.1\n            - **Mod Loader**: NeoForge\n            - **Workflow Run**: [#${{ github.run_number }}](${context.payload.repository.html_url}/actions/runs/${context.runId})\n            \n            ### Test Parameters\n            \n            The fuzzing test uses random parameters. Check the workflow logs for specific test configuration used (network size, number of parts, operator depth).\n            \n            ### How to Debug\n            \n            1. Download the structure file\n            2. Copy it to \\`client/saves/<worldname>/generated/minecraft/structures/<template>.nbt\\`\n            3. Spawn a structure block: \\`/give @p minecraft:structure_block\\`\n            4. Load the structure in the structure block GUI: \\`minecraft:<template>\\`\n            \n            ### Additional Context\n            \n            This issue was automatically generated by the fuzzing test workflow. For more details, see the [full workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId}).\n            \n            If this is a false positive or environmental issue, please close this issue and mark it accordingly.\n            `;\n\n            // Create the issue\n            const issue = await github.rest.issues.create({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              title: `🐛 Fuzzing detected crash in network generation (run #${{ github.run_number }})`,\n              body: issueBody,\n              labels: ['bug', 'fuzzing'],\n              assignees: []\n            });\n            \n            console.log(`Created issue #${issue.data.number}`);\n            core.setOutput('issue-number', issue.data.number);\n\n"
  },
  {
    "path": ".github/workflows/issue-label-commenter.yml",
    "content": "name: \"Comment on issues by label\"\non:\n  issues:\n    types: [labeled]\n\njobs:\n  comment:\n    runs-on: ubuntu-latest\n    steps:\n    - name: get comment count\n      id: getCommentCount\n      env:\n        comments: ${{ github.event.issue.comments }}\n      run: |\n        echo \"$comments\"\n        CL=$(echo $comments | jq '. | length' )\n        echo \"::set-output name=comments_length::$CL\"\n    - uses: actions/checkout@v2\n      if: steps.getCommentCount.outputs.comments_length == 0\n    - uses: peaceiris/actions-label-commenter@v1\n      if: steps.getCommentCount.outputs.comments_length == 0\n      with:\n        github_token: ${{ secrets.PAT }}\n        config_file: .github/issue-commenter.yml\n"
  },
  {
    "path": ".github/workflows/issue-label-to-project-board.yml",
    "content": "name: \"Add labeled issues to project board\"\non:\n  issues:\n    types: [labeled]\n\njobs:\n  assign:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Maintenance Issues\n        id: add-project-maintenance\n        uses: actions/add-to-project@v1.0.1\n        with:\n          project-url: https://github.com/orgs/CyclopsMC/projects/3\n          github-token: ${{ secrets.PAT }}\n          labeled: bug, performance, question, more-information-needed\n          label-operator: OR\n      - name: Development Issues\n        id: add-project-development\n        uses: actions/add-to-project@v1.0.1\n        with:\n          project-url: https://github.com/orgs/CyclopsMC/projects/4\n          github-token: ${{ secrets.PAT }}\n          labeled: feature-long-term, feature\n          label-operator: OR\n      - name: Feature Accepted\n        if: |\n          contains(github.event.issue.labels.*.name, 'feature')\n        uses: titoportas/update-project-fields@v0.1.0\n        with:\n          project-url: https://github.com/orgs/CyclopsMC/projects/4\n          github-token: ${{ secrets.PAT }}\n          item-id: ${{ steps.add-project-development.outputs.itemId }}\n          field-keys: Status\n          field-values: 'Accepted (To Do)'\n      - name: More Information Needed\n        if: |\n          contains(github.event.issue.labels.*.name, 'more-information-needed')\n        uses: titoportas/update-project-fields@v0.1.0\n        with:\n          project-url: https://github.com/orgs/CyclopsMC/projects/3\n          github-token: ${{ secrets.PAT }}\n          item-id: ${{ steps.add-project-maintenance.outputs.itemId }}\n          field-keys: Status\n          field-values: 'On hold (awaiting input)'\n"
  },
  {
    "path": ".github/workflows/new-issue-label.yml",
    "content": "name: \"Label new issues\"\non:\n  issues:\n    types: [opened, edited]\n\njobs:\n  triage:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: github/issue-labeler@v3.4\n      with:\n        repo-token: \"${{ secrets.PAT }}\"\n        configuration-path: .github/labeler.yml\n        enable-versioned-regex: 0\n"
  },
  {
    "path": ".github/workflows/performance.yml",
    "content": "name: \"Performance\"\non: [push, pull_request]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  benchmark:\n    name: Benchmark\n    if: github.event.repository.fork != true && (startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/feature'))\n    runs-on: ubuntu-latest\n    steps:\n      - name: 'Checkout'\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n      - name: 'Setup Java'\n        uses: actions/setup-java@v4\n        with:\n          distribution: 'microsoft'\n          java-version: 21\n      - name: 'Setup Gradle'\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-version: wrapper\n          cache-read-only: false\n      - name: 'Build'\n        run: ./gradlew build -x test --no-daemon\n        env:\n          GITHUB_USER: ${{ github.actor }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: 'Run performance benchmarks'\n        run: ./gradlew runGameTestServer\n        env:\n          PERFORMANCE_BENCHMARK_ENABLED: 'true'\n        continue-on-error: true\n      - name: 'Parse benchmark results'\n        run: bash .github/parse_benchmark_results.sh\n      - name: Determine benchmarks total results target directory name\n        run: echo \"BENCHMARK_DATA_DIR_PATH=CyclopsMC/${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}/benchmarks\" >> $GITHUB_ENV\n      - name: 'Store benchmark result'\n        uses: benchmark-action/github-action-benchmark@v1\n        with:\n          name: Integrated Dynamics Network Benchmark\n          tool: customSmallerIsBetter\n          output-file-path: benchmark_results.json\n          github-token: ${{ secrets.PAT }}\n          auto-push: true\n          alert-threshold: '250%'\n          comment-always: true\n          comment-on-alert: true\n          alert-comment-cc-users: '@rubensworks'\n          summary-always: true\n          gh-repository: github.com/CyclopsMC/cyclops-performance-results\n          gh-pages-branch: master\n          benchmark-data-dir-path: ${{ env.BENCHMARK_DATA_DIR_PATH }}\n      - name: Prepare comment on commit with link to performance results\n        run: echo -e \"Performance benchmarks succeeded! 🚀\\n\\n\\[[Results](https://CyclopsMC.github.io/cyclops-performance-results/CyclopsMC/${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}/benchmarks/)\\]\" > ./commit-comment-body.txt\n      - name: Comment on commit with link to performance results\n        uses: peter-evans/commit-comment@v4\n        with:\n          body-path: ./commit-comment-body.txt\n\n"
  },
  {
    "path": ".github/workflows/publish-github-release.yml",
    "content": "name: Publish GitHub release on version tags\non:\n  push:\n    tags:\n      - '*'\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Release\n        uses: softprops/action-gh-release@v2\n        with:\n          body_path: CHANGELOG.md\n"
  },
  {
    "path": ".gitignore",
    "content": "# We don't want compiled stuff in here!\n/bin\ndoc/info.txt\n/build\n/run\n/runs\n/logs\n\n# Ignore project specific files\n.classpath\n.project\n.settings\n.pydevproject\neclipse/*\n.gradle/*\nrepo/*\n*.iml\n*.ipr\n*.iws\nout/*\nkeystore.jks\n.idea\nlogs/\n\n# Ignore IDE specific files\n.vscode\n*.launch\n\n# Ignore mac-specific file(s)\n.DS_Store\n\n# Ignore files specific to dev environments\nsecrets.properties\nchangelog.txt\n\n.cache\nconfig\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"src/main/java/org/cyclops/commoncapabilities/api\"]\n\tpath = src/main/java/org/cyclops/commoncapabilities/api\n\turl = https://github.com/CyclopsMC/CommonCapabilitiesAPI.git\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# AGENTS.md - Developer and AI Agent Guide\n\nThis document provides essential information for developers and AI agents working on this Minecraft mod's codebase.\n\n## Project Overview\n\nThis is a Minecraft mod. See README.md for a description of what this mod does. The project is written in Java and uses Gradle as its build system.\n\n## Multi-Loader Architecture\n\n**This repo only uses a multi-loader architecture if you can find a `loader-common` directory in the root of this repo!**\n\nIn Minecraft 1.21 and above, this repository uses a **multi-loader setup**, meaning the mod is available on multiple Minecraft mod loaders.\nThe Minecraft version can be found in `gradle.properties`.\nUnderstanding this architecture is crucial when making changes:\n\n### Directory Structure\n\n- **`loader-common/`**: Contains source code that is common across all mod loaders. Most shared functionality should be implemented here.\n- **`loader-fabric/`**: Fabric-specific implementation and integration code.\n- **`loader-forge/`**: Forge-specific implementation and integration code (for older Minecraft versions).\n- **`loader-neoforge/`**: NeoForge-specific implementation and integration code (for newer Minecraft versions).\n\nFor NeoForge, relevant jars can for example be found in the following locations:\n\n- Minecraft merged jar: `build/moddev/artifacts/minecraft-patched-26.1.1.1-beta-merged.jar`\n- NeoForge: `~/.gradle/caches/modules-2/files-2.1/net.neoforged/neoforge/26.1.1.1-beta/214ad5aa883deaf6d71298baf4232bb994305f36/neoforge-26.1.1.1-beta-universal.jar`\n- CyclopsCore source: `~/.m2/repository/org/cyclops/cyclopscore/cyclopscore-26.1.1-neoforge/1.25.5-DEV/cyclopscore-26.1.1-neoforge-1.25.5-DEV-sources.jar`\n\n### Making Changes in Multi-loader Setups\n\nWhen adding features or fixing bugs:\n1. Place shared logic in `loader-common/` whenever possible\n2. Only add loader-specific code to the respective `loader-*` directories when platform-specific APIs are required\n3. Ensure your changes work across all supported loaders\n\n## Testing\n\nThis mod uses two types of tests:\n\n### 1. Unit Tests\n\n**Location**: `src/test/java/`\n\nTraditional JUnit tests for testing isolated functionality without requiring a full Minecraft instance.\n\n**Running unit tests**:\n```bash\n./gradlew test\n```\n\nUnit tests are automatically executed when running the `build` command.\n\n### 2. Game Tests\n\n**Location**: Within normal sources, typically in the `org/cyclops/*/gametest` package (e.g., `loader-common/src/main/java/org/cyclops/cyclopscore/gametest/`)\n\nGame tests run an actual Minecraft instance to test code with real game logic. These are essential for testing features that interact with Minecraft's gameplay systems.\nGame tests only exist in Minecraft 1.21 and higher.\n\n**Running game tests**:\n```bash\n./gradlew runGameTestServer\n```\n\n**Important**:\n- Game tests are **NOT** run automatically during the build process\n- For Minecraft 1.21 and above, game tests must be run manually before committing\n- Game tests must pass before finalizing your changes\n\n### When to Add Tests\n\nWhen adding new features or fixing bugs:\n- **Always** add unit tests when possible for isolated logic\n- **Always** add game tests when the feature interacts with Minecraft gameplay systems\n- Look at existing tests in the respective directories for examples of test patterns and conventions\n\n### Testing advancements\n\nWhen writing game tests for advancements, don't just call criterion triggers directly, but try to simulate actual game logic to invoke the criterion triggers indirectly.\n\n### Test coverage\n\nWhen adding new tests for the sake of increasing test coverage, you can measure coverage by running `./gradlew test runGameTestServer jacocoTestReport`, and checking the coverage output in `build/reports/jacoco/test/`.\n\n## Building the Project\n\n### Prerequisites\n\n- Java version is specified in `gradle.properties` (otherwise, default to version 17)\n- Gradle (use the provided wrapper: `./gradlew`)\n\n### Build Command\n\nBefore every commit, ensure the project builds successfully:\n\n```bash\n./gradlew build\n```\n\nThis command will:\n- Compile all source code\n- Run unit tests automatically\n- Generate build artifacts\n\n### Full Pre-Commit Validation\n\nRun build:\n\n```bash\n./gradlew build\n```\n\nOnly for Minecraft 1.21 and above, also run game tests:\n```bash\n./gradlew runGameTestServer\n```\n\nBoth must pass before committing changes.\n\n## Code Formatting\n\nThis project uses Spotless for code formatting:\n\n```bash\n./gradlew spotlessApply\n```\n\nThe pre-commit script in `scripts/pre-commit` automatically formats staged files. Consider setting it up as a Git hook:\n\n```bash\nln -s ../../scripts/pre-commit .git/hooks/pre-commit\n```\n\nIn general, try to use imports where possible.\nSo avoid inlining things in like `net.minecraft.world.phys.Vec3` in the code, and instead, just add an import for `net.minecraft.world.phys.Vec3`.\n\n## Development Workflow\n\n1. **Understand the change**: Read the issue/feature request thoroughly\n2. **Explore the codebase**: Use tools like `grep` to find relevant code\n3. **Make minimal changes**: Focus on the specific issue/feature\n4. **Add tests**: Write unit tests and/or game tests as appropriate\n5. **Build and test**:\n   ```bash\n   ./gradlew build\n   ./gradlew runGameTestServer  # For MC 1.21+\n   ```\n6. **Format code**: `./gradlew spotlessApply`\n7. **Commit**: Use clear, descriptive commit messages\n\n## Release Management\n\nVersion bumping and release management helper scripts are available in the [CyclopsMC/ReleaseHelpers](https://github.com/CyclopsMC/ReleaseHelpers) repository. These bash scripts assist with:\n- Version bumping across all loaders\n- Changelog management\n- Release preparation\n\n## Gradle Tasks Reference\n\nCommon Gradle tasks for development:\n\n| Task | Purpose |\n|------|---------|\n| `./gradlew build` | Build the project and run unit tests |\n| `./gradlew test` | Run unit tests only |\n| `./gradlew runGameTestServer` | Run game tests (manual, required for MC 1.21+) |\n| `./gradlew spotlessApply` | Format code according to project standards |\n| `./gradlew publishToMavenLocal` | Publish to local Maven for testing in other projects |\n\nIf for any reason gradle fails due to internet connection issues, try running offline instead by running the gradle command with the `--offline` flag.\n\n## CI/CD\n\nGitHub Actions automatically:\n- Builds the project on every push and pull request\n- Runs unit tests\n- Runs game tests (including `runGameTestServer`)\n- Generates coverage reports\n- Deploys to CurseForge, Modrinth, and Maven on appropriate branches/tags\n\nSee `.github/workflows/ci.yml` for the full CI configuration.\n\n## Additional Resources\n\n- **README.md**: Project overview and usage information\n- **CONTRIBUTING.md**: Contribution guidelines and issue reporting\n- **Build configuration**: `build.gradle` and loader-specific build files\n- **Project properties**: `gradle.properties` (Minecraft version, mod version, etc.)\n\n## Key Principles\n\n1. **Minimal changes**: Make the smallest possible changes to achieve your goal\n2. **Test coverage**: Always try to add tests for new features and bug fixes (may be difficult or impossible for things related to GUIs)\n3. **Multi-loader compatibility**: Ensure changes work across all supported loaders\n4. **Build validation**: Never commit without running `build` (and `runGameTestServer` for MC 1.21+)\n5. **Code quality**: Follow existing patterns and conventions in the codebase\n\n## Code changes across Minecraft updates\n\nWhen I ask you to things such as \"fix upmerge issues\" or \"update to the next Minecraft version\",\nthis means that I want you to help resolve compilation errors\nthat were introduced due to upmerging code from an older minecraft version to a newer minecraft version.\nThe old minecraft version can be found in the `.upmerge-src-branch` file of the parent directory,\nand the new minecraft version can be found in `gradle.properties`.\nChanges may also include fixing merge conflicts.\n\nIMPORTANT: Some changes may be non-trivial,\nfor which you can find detail background information for each seperate Minecraft update within these primers: https://github.com/neoforged/.github/tree/main/primers\nThe blogs of https://neoforged.net/ and https://fabricmc.net/blog/ may also contain useful porting help.\n\nAfter making all necessary changes, make sure the code fully compiles and the (game) tests pass.\n"
  },
  {
    "path": "CHANGELOG-1.19.2.md",
    "content": "# Changelog for Minecraft 1.19.2\nAll notable changes to this project will be documented in this file.\n\n<a name=\"1.19.2-1.30.1\"></a>\n## [1.19.2-1.30.1](/compare/1.19.2-1.30.0...1.19.2-1.30.1) - 2026-03-28 09:54:36\n\n\n### Added\n* Add configurable energy production rate for coal generator (#1635), Closes #1634\n\n<a name=\"1.19.2-1.30.0\"></a>\n## [1.19.2-1.30.0](/compare/1.19.2-1.29.3...1.19.2-1.30.0) - 2026-02-17 10:58:05 +0100\n\n\n### Added\n* Document logical NBT expressions, CyclopsMC/CyclopsCore#210\n* Add config options for minimum update intervals for parts, Closes CyclopsMC/IntegratedTunnels#349\n* Add config option to log card IDs during evaluation, Closes #1345\n\n<a name=\"1.19.2-1.29.3\"></a>\n## [1.19.2-1.29.3](/compare/1.19.2-1.29.2...1.19.2-1.29.3) - 2026-01-17 14:00:53 +0100\n\n\n### Added\n* Add generic number casting operators\n  Closes CyclopsMC/IntegratedScripting#55\n  These new operators make it more convenient to work with return types of\n  Integrated Scripting, where numbers could be integers, doubles, or\n  longs.\n\n### Changed\n* Add safety check for infinite lists in list equals and itemstack count\n  Closes #1583\n  Related to #1582\n\n### Fixed\n* Fix part states not being revalidated, Closes #1588\n* Fix oldest/newest mixup on delayer history in book, Closes #1592\n\n<a name=\"1.19.2-1.29.2\"></a>\n## [1.19.2-1.29.2](/compare/1.19.2-1.29.1...1.19.2-1.29.2) - 2025-12-28 09:16:36 +0100\n\n\n### Fixed\n* Add safety checks to disable some operators on infinite lists\n  Concretely, contains, reduce, and uniq are disabled on infinite lists.\n  Closes #1582\n\n<a name=\"1.19.2-1.29.1\"></a>\n## [1.19.2-1.29.1](/compare/1.19.2-1.29.0...1.19.2-1.29.1) - 2025-12-23 09:15:43 +0100\n\n\n### Fixed\n* Fix facade squeezer recipes breaking infobook\n\n<a name=\"1.19.2-1.29.0\"></a>\n## [1.19.2-1.29.0](/compare/1.19.2-1.28.6...1.19.2-1.29.0) - 2025-12-23 09:04:18 +0100\n\n\n### Added\n* Add squeezer recipes for clearing facades\n\n<a name=\"1.19.2-1.28.6\"></a>\n## [1.19.2-1.28.6](/compare/1.19.2-1.28.5...1.19.2-1.28.6) - 2025-11-25 16:57:54 +0100\n\n\n### Fixed\n* Restore network element chunk unloading again, Closes #1571\n\n<a name=\"1.19.2-1.28.5\"></a>\n## [1.19.2-1.28.5](/compare/1.19.2-1.28.4...1.19.2-1.28.5) - 2025-11-24 16:56:01 +0100\n\n\n### Fixed\n* Temporarily disable chunk unload check again\n\nIt is causing issues with delayers after chunk reloaded.\nOnce a proper fix is available, this will be re-enabled.\n\nRelated to #1571\n\n<a name=\"1.19.2-1.28.4\"></a>\n## [1.19.2-1.28.4](/compare/1.19.2-1.28.3...1.19.2-1.28.4) - 2025-11-22 11:25:24 +0100\n\n\n### Fixed\n* Restore network element chunk unloading\n  This was temporarily disabled as it broke delayers and proxies on\n  world/chunk reload, which this commit fixes.\n  Closes #1571\n\n<a name=\"1.19.2-1.28.3\"></a>\n## [1.19.2-1.28.3](/compare/1.19.2-1.28.2...1.19.2-1.28.3) - 2025-11-18 18:46:25 +0100\n\n\n### Fixed\n* Temporarily disable chunk unload check\n  It is causing issues with delayers when worlds/chunks are reloaded.\n  Once a proper fix is available, this will be re-enabled.\n  Related to #1571\n\n<a name=\"1.19.2-1.28.2\"></a>\n## [1.19.2-1.28.2](/compare/1.19.2-1.28.1...1.19.2-1.28.2) - 2025-11-18 05:31:19 +0100\n\n\n### Fixed\n* Fix read fluids not always updating in display panel, Closes #1570\n\n<a name=\"1.19.2-1.28.1\"></a>\n## [1.19.2-1.28.1](/compare/1.19.2-1.28.0...1.19.2-1.28.1) - 2025-11-16 14:29:27 +0100\n\n\n### Fixed\n* Fix ticking network elements preventing chunk unloading, Closes #1567\n\n<a name=\"1.19.2-1.28.0\"></a>\n## [1.19.2-1.28.0](/compare/1.19.2-1.27.6...1.19.2-1.28.0) - 2025-11-11 13:55:36 +0100\n\n\n### Added\n* Add fluid by name operator, Closes #1522\n\n### Changed\n* Ensure consistent order of ingredient types in LP, Closes #1539\n\n### Fixed\n* Fix transparent facades having incorrect alpha value, Closes #1563\n* Fix lists materialization not applying to elements\n  This could cause issues when displaying lists of items from inventory\n  readers over Functional Storage Drawers.\n  Closes #1557\n\n<a name=\"1.19.2-1.27.6\"></a>\n## [1.19.2-1.27.6](/compare/1.19.2-1.27.5...1.19.2-1.27.6) - 2025-10-11 11:42:33 +0200\n\n\n### Fixed\n* Fix memory leak when reloading offset variables, Closes #1549\n* Handle overflows in fluid reader total aspects, Closes #1547\n\n<a name=\"1.19.2-1.27.5\"></a>\n## [1.19.2-1.27.5](/compare/1.19.2-1.27.4...1.19.2-1.27.5) - 2025-07-18 20:36:02 +0200\n\n\n### Fixed\n* Fix crash on load operator variable in LP, Closes #1537\n\n<a name=\"1.19.2-1.27.4\"></a>\n## [1.19.2-1.27.4](/compare/1.19.2-1.27.3...1.19.2-1.27.4) - 2025-07-05 06:54:35 +0200\n\n\n### Fixed\n* Fix LP crash when loading with empty category card, Closes #1527\n\n<a name=\"1.19.2-1.27.3\"></a>\n## [1.19.2-1.27.3](/compare/1.19.2-1.27.2...1.19.2-1.27.3) - 2025-06-21 13:21:39 +0200\n\n\n### Changed\n* Return item to player instead of dropping on Labeler exit, Closes #1526\n\n### Fixed\n* Fix fluidstack operator crash on empty items, Closes #1525\n\n<a name=\"1.19.2-1.27.2\"></a>\n## [1.19.2-1.27.2](/compare/1.19.2-1.27.1...1.19.2-1.27.2) - 2025-06-12 16:35:33 +0200\n\n\n### Fixed\n* Fix cables not being breakable with wrench, Closes #1523\n\n<a name=\"1.19.2-1.27.1\"></a>\n## [1.19.2-1.27.1](/compare/1.19.2-1.27.0...1.19.2-1.27.1) - 2025-06-07 18:00:46 +0200\n\n\n### Changed\n* Only switch Wrench mode when aiming at air\n  Closes #1518\n  Closes #1390\n\n### Fixed\n* Fix overlapping tooltips in LP write slot, Closes #1519\n\n<a name=\"1.19.2-1.27.0\"></a>\n## [1.19.2-1.27.0](/compare/1.19.2-1.26.1...1.19.2-1.27.0) - 2025-05-31 19:36:49 +0200\n\n\n### Added\n* Allow editing existing variables in Logic Programmer, Closes #357\n* Add Variable Value By ID operator aspect to network reader, Closes #1346\n* Add button to reset selected LP element\n\n### Changed\n* Don't drop item in world when closing LP\n* Improve LP element arrow\n\n### Fixed\n* Fix operator text field in LP sometimes being red\n\n<a name=\"1.19.2-1.26.1\"></a>\n## [1.19.2-1.26.1](/compare/1.19.2-1.26.0...1.19.2-1.26.1) - 2025-05-20 17:28:11 +0200\n\n\n### Fixed\n* Fix search hotkey also typing in hotkey in search box\n  Closes CyclopsMC/IntegratedTerminals#168\n\n<a name=\"1.19.2-1.26.0\"></a>\n## [1.19.2-1.26.0](/compare/1.19.2-1.25.5...1.19.2-1.26.0) - 2025-05-03 15:33:50 +0200\n\n\n### Added\n* Add list set and multiset equality operators, Closes #1347\n* Add Fluid with tag operator, Closes #797\n* Add block and fluid tag operators, Closes #1394\n* Add Sqrt and Pow operators, Closes #1372\n* Add lectern and bookshelf support for infobook, Closes #1496\n\n### Fixed\n* Fix non-fluid items being insertable into LP, Closes #1507\n\n<a name=\"1.19.2-1.25.5\"></a>\n## [1.19.2-1.25.5](/compare/1.19.2-1.25.4...1.19.2-1.25.5) - 2025-03-22 15:12:36 +0100\n\n\n### Fixed\n* Fix NBT from int list not working for any lists\n  Closes CyclopsMC/IntegratedScripting#37\n* Fix typos in manual\n* Fixed number typo\n* Fixed variable description\n* Fixed typo in reader introduction\n\n<a name=\"1.19.2-1.25.4\"></a>\n## [1.19.2-1.25.4](/compare/1.19.2-1.25.3...1.19.2-1.25.4) - 2025-03-10 06:57:05 +0100\n\n\n### Changed\n* Avoid repeated log spam for forcefully unloaded parts, Closes #1481\n\n<a name=\"1.19.2-1.25.3\"></a>\n## [1.19.2-1.25.3](/compare/1.19.2-1.25.2...1.19.2-1.25.3) - 2025-03-01 07:05:09 +0100\n\n\n### Changed\n* Improve performance of block changes to very large networks\n  Pathfinder is ~70x faster, also improved tick speed with lots of devices (17 ms to 3 ms in test scene)\n\n### Fixed\n* Fix duplication of offset enchancements when breaking cable, Closes #1480\n* Fix passive interaction changes not immediately applying on change\n  It used to only take effect after re-inserting a variable card.\n  Closes CyclopsMC/IntegratedDynamics#1470\n\n<a name=\"1.19.2-1.25.2\"></a>\n## [1.19.2-1.25.2](/compare/1.19.2-1.25.1...1.19.2-1.25.2) - 2025-02-22 17:02:54 +0100\n\n\n### Fixed\n* Fix GUIs remaining open after external breakage, Closes #1472\n* Fix rare tooltip crash for item-based variable cards, Closes #1477\n\n<a name=\"1.19.2-1.25.1\"></a>\n## [1.19.2-1.25.1](/compare/1.19.2-1.25.0...1.19.2-1.25.1) - 2025-02-03 16:58:31 +0100\n\n\n### Added\n* Add Item with tag operation\n\n### Fixed\n* Fix REI recipe transfer to LP not working for fluids\n* Fix LP recipe transfer including chance-based outputs\n  Non-1000utputs could cause issues for autocrafting.\n  This is fixed for both JEI and REI.\n  Closes CyclopsMC/IntegratedCrafting#127\n* Fix tag-based JEI recipe transfer ignoring stack sizes\n  Closes CyclopsMC/IntegratedCrafting#128\n* Fix placing part before cable not properly connecting cables\n  Closes CyclopsMC/IntegratedTunnels#321\n\n<a name=\"1.19.2-1.25.0\"></a>\n## [1.19.2-1.25.0](/compare/1.19.2-1.24.4...1.19.2-1.25.0) - 2025-01-08 17:29:20 +0100\n\n\n### Added\n* Add item tooltip operators\n\n<a name=\"1.19.2-1.24.4\"></a>\n## [1.19.2-1.24.4](/compare/1.19.2-1.24.3...1.19.2-1.24.4) - 2024-12-28 13:55:25 +0100\n\n\n### Added\n* Add lossy recipe to clear a facade in a crafting grid, Closes #1424\n\n### Fixed\n* Fix client-server desync when applying part offsets, Closes #1448\n* Fix offset items only being applicable to 28 instead of 32, #1448\n\n<a name=\"1.19.2-1.24.3\"></a>\n## [1.19.2-1.24.3](/compare/1.19.2-1.24.2...1.19.2-1.24.3) - 2024-12-24 09:53:00 +0100\n\n\n### Fixed\n* Reduce number of network events during init to improve performance, #1439\n  Modifying very large networks is a lot faster now.\n\n<a name=\"1.19.2-1.24.2\"></a>\n## [1.19.2-1.24.2](/compare/1.19.2-1.24.1...1.19.2-1.24.2) - 2024-12-17 11:09:48 +0100\n\n\n### Added\n* Add error operator\n\n### Changed\n* Reduce network inits by half when placing cables, Closes #1439\n\n<a name=\"1.19.2-1.24.1\"></a>\n## [1.19.2-1.24.1](/compare/1.19.2-1.24.0...1.19.2-1.24.1) - 2024-12-06 15:57:57 +0100\n\n\n### Fixed\n* Fix cable placement with commands initializing networks\n  This was broken since CyclopsMC/IntegratedTunnels#243\n\n<a name=\"1.19.2-1.24.0\"></a>\n## [1.19.2-1.24.0](/compare/1.19.2-1.23.11...1.19.2-1.24.0) - 2024-12-06 15:55:09 +0100\n\n\n### Added\n* Add dedicated REI support, Closes CyclopsMC/IntegratedDynamics#1348\n* Restore Refined Storage integration\n* Restore Jade/Waila integration, Closes CyclopsMC/IntegratedDynamics#1413\n\n<a name=\"1.19.2-1.23.11\"></a>\n## [1.19.2-1.23.11](/compare/1.19.2-1.23.10...1.19.2-1.23.11) - 2024-11-19 15:17:19 +0100\n\n\n### Changed\n* Drop Part Offsets into their original form, Closes #1418\n\n### Fixed\n* Fix creative batteries not providing energy, Closes #1421\n\n<a name=\"1.19.2-1.23.10\"></a>\n## [1.19.2-1.23.10](/compare/1.19.2-1.23.9...1.19.2-1.23.10) - 2024-11-10 14:10:06 +0100\n\n\n### Fixed\n* Fix variables dependent on offset aspects not always updating, Closes #1416\n\n<a name=\"1.19.2-1.23.9\"></a>\n## [1.19.2-1.23.9](/compare/1.19.2-1.23.8...1.19.2-1.23.9) - 2024-11-02 15:59:15 +0100\n\n\n### Changed\n* Optimize ingredient positions index lookups\n  Closes CyclopsMC/IntegratedTunnels#307\n\n### Fixed\n* Fix Deepslate Dark Ore not being squeezable, Closes #1414\n\n<a name=\"1.19.2-1.23.8\"></a>\n## [1.19.2-1.23.8](/compare/1.19.2-1.23.7...1.19.2-1.23.8) - 2024-10-30 18:31:17 +0100\n\n\n### Fixed\n* Fix crash with complex Integrated Scripting functions in writers\n\nFunctions returning an any type could be inserted into writers such as\nIntegrated Tunnels exporters, and type checking would incorrectly pass.\nThis could result in crashes where an incorrect value cast would occur.\nThis commit makes it so that ANY types will have an additional type\ncheck based on the actual determined value.\n\nCloses CyclopsMC/IntegratedScripting#20\n\n<a name=\"1.19.2-1.23.7\"></a>\n## [1.19.2-1.23.7](/compare/1.19.2-1.23.6...1.19.2-1.23.7) - 2024-10-29 18:49:21 +0100\n\n\n### Fixed\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 61b7372bda5cde123a2b320ac92c1c24d2dfb9b1.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n<a name=\"1.19.2-1.23.6\"></a>\n## [1.19.2-1.23.6](/compare/1.19.2-1.23.5...1.19.2-1.23.6) - 2024-10-28 17:29:04 +0100\n\n\n### Changed\n* Optimize ingredient positions index\n  This improves performance with Integrated Tunnels and Terminals.\n\n<a name=\"1.19.2-1.23.5\"></a>\n## [1.19.2-1.23.5](/compare/1.19.2-1.23.4...1.19.2-1.23.5) - 2024-10-24 16:53:11 +0200\n\n\n### Fixed\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n\n<a name=\"1.19.2-1.23.4\"></a>\n## [1.19.2-1.23.4](/compare/1.19.2-1.23.3...1.19.2-1.23.4) - 2024-10-14 15:14:52 +0200\n\n\n### Fixed\n* Fix incorrect type checking in complex reduce operation, Closes #1387\n\n<a name=\"1.19.2-1.23.3\"></a>\n## [1.19.2-1.23.3](/compare/1.19.2-1.23.2...1.19.2-1.23.3) - 2024-10-03 18:58:24 +0200\n\n\n### Fixed\n* Fix JEI ghosts items not working for lists in the LP, Closes CyclopsMC/IntegratedDynamics#1398\n* Fix wrong sided part being shown in The One Probe, Closes #1401\n* Fix round-robin misbehaving with filtered interfaces, Closes CyclopsMC/IntegratedTunnels#302\n\n<a name=\"1.19.2-1.23.2\"></a>\n## [1.19.2-1.23.2](/compare/1.19.2-1.23.1...1.19.2-1.23.2) - 2024-09-02 18:03:23 +0200\n\n\n### Fixed\n* Delay Terrablender registration\n  This fixes rare crashes when Terrablender was not yet fully initialized.\n  CyclopsMC/IntegratedDynamics#1385\n  Closes CyclopsMC/IntegratedDynamics#1388\n\n<a name=\"1.19.2-1.23.1\"></a>\n## [1.19.2-1.23.1](/compare/1.19.2-1.23.0...1.19.2-1.23.1) - 2024-08-22 18:56:55 +0200\n\n\n### Fixed\n* Fix op_by_name crashing if ResourceLocation is invalid, Closes #1381\n* Fix regex scan producing illegal lists for non-zero groups, Closes #1378\n\n<a name=\"1.19.2-1.23.0\"></a>\n## [1.19.2-1.23.0](/compare/1.19.2-1.22.2...1.19.2-1.23.0) - 2024-07-31 15:14:30 +0200\n\n\n### Added\n* Add Terrablender compat for Meneglin biome\n\n<a name=\"1.19.2-1.22.2\"></a>\n## [1.19.2-1.22.2](/compare/1.19.2-1.22.1...1.19.2-1.22.2) - 2024-07-21 11:35:23 +0200\n\n\n### Fixed\n* Interrupt speech before sending a new one to the narrator, Closes #1356\n\n<a name=\"1.19.2-1.22.1\"></a>\n## [1.19.2-1.22.1](/compare/1.19.2-1.22.0...1.19.2-1.22.1) - 2024-06-24 10:16:41 +0200\n\n\n### Changed\n* Also copy label when copying variable cards, Closes #1354\n* Allow Ingredient.with* operators to have empty ingredient inputs\n* Allow entering items in logic programmer with larger stack sizes\n\n### Fixed\n* Fix default labeller textfield being modifiable, Closes #1352\n* Fix apply_0 crashing on operators with input, Closes CyclopsMC/IntegratedScripting#11\n* Fix incorrect comparator in PrioritizedPartPos\n\n<a name=\"1.19.2-1.22.0\"></a>\n## [1.19.2-1.22.0](/compare/1.19.2-1.21.3...1.19.2-1.22.0) - 2024-04-28 10:00:19 +0200\n\n\n### Added\n* Allow checking if there are variable-based offsets in API\n  Required for CyclopsMC/IntegratedTunnels#289\n\n<a name=\"1.19.2-1.21.3\"></a>\n## [1.19.2-1.21.3](/compare/1.19.2-1.21.2...1.19.2-1.21.3) - 2024-04-14 14:02:05 +0200\n\n\n### Changed\n* Allow listening to EvaluationException resolutions\n  Required for CyclopsMC/IntegratedScripting#5\n\n### Fixed\n* Fix crash for illegal item tag chars in recipe in LP, Closes CyclopsMC/IntegratedCrafting#103\n* Fix Dank Storage item removal when placing cables, Closes #1332\n* Fix facades being craftable for non-solid blocks, Closes #1334, #1342\n\n<a name=\"1.19.2-1.21.2\"></a>\n## [1.19.2-1.21.2](/compare/1.19.2-1.21.1...1.19.2-1.21.2) - 2024-02-11 17:00:37 +0100\n\n\n### Fixed\n* Fix tps and ticktime aspects returning values different to Forge, Closes #1325\n\n<a name=\"1.19.2-1.21.1\"></a>\n## [1.19.2-1.21.1](/compare/1.19.2-1.21.0...1.19.2-1.21.1) - 2024-02-07 19:14:35 +0100\n\n\n### Fixed\n* Fix part offsets sometimes being delayed by one aspect update, Closes #1320\n* Fix errored offset variables not resetting after network reset, Closes #1321\n\n<a name=\"1.19.2-1.21.0\"></a>\n## [1.19.2-1.21.0](/compare/1.19.2-1.20.3...1.19.2-1.21.0) - 2024-02-04 14:50:26 +0100\n\n\n### Added\n* Expose interact names for operators. This is required for Integrated Scripting.\n* Add apply_0 operator\n* Add apply_n operator\n\n### Changed\n* Add INetwork parameter to variable facade methods\n\n### Fixed\n* Fix duplicate variable invalidate listeners being registered\n  This could leak to memory leaks for long-running multi-arg operators.\n\n<a name=\"1.19.2-1.20.3\"></a>\n## [1.19.2-1.20.3](/compare/1.19.2-1.20.2...1.19.2-1.20.3) - 2023-12-27 17:21:07 +0100\n\n\n### Fixed\n* Fix hard crash on replace_regex with invalid group, Closes #1317\n\n<a name=\"1.19.2-1.20.2\"></a>\n## [1.19.2-1.20.2](/compare/1.19.2-1.20.1...1.19.2-1.20.2) - 2023-12-04 11:05:31 +0100\n\n\n### Fixed\n* Fix predicate-based filtering interfaces causing ingredient loss\n  Integrated Tunnels filtering interaces could lose items\n  when using predicate-based filters.\n  Closes CyclopsMC/IntegratedTunnels#282\n\n<a name=\"1.19.2-1.20.1\"></a>\n## [1.19.2-1.20.1](/compare/1.19.2-1.20.0...1.19.2-1.20.1) - 2023-11-27 15:50:25 +0100\n\n\n### Fixed\n* Fix NBT.from_tag_list not working on Any lists, Closes #1315\n\n<a name=\"1.19.2-1.20.0\"></a>\n## [1.19.2-1.20.0](/compare/1.19.2-1.19.1...1.19.2-1.20.0) - 2023-10-10 16:53:35 +0200\n\n\n### Added\n* Add initialChange flag to storage change events\n  This is required to fix CyclopsMC/IntegratedCrafting#99\n\n<a name=\"1.19.2-1.19.1\"></a>\n## [1.19.2-1.19.1](/compare/1.19.2-1.19.0...1.19.2-1.19.1) - 2023-09-24 11:39:38 +0200\n\n\n### Fixed\n* Fix held items vanishing after gui close, Closes #1308\n\n<a name=\"1.19.2-1.19.0\"></a>\n## [1.19.2-1.19.0](/compare/1.19.2-1.18.0...1.19.2-1.19.0) - 2023-09-12 19:44:06 +0200\n\n\n### Added\n* Expose positions from channels\n  This is required for CyclopsMC/IntegratedCrafting#98\n\n### Fixed\n* Fix predicate-based tunnels movement ignoring channels, Closes CyclopsMC/IntegratedTunnels#274\n* Fix crash on special operator with ANY type, Closes #1301\n* Fix Menril Tree replacing grass with dirt on stump place fail, Closes #1304\n\n<a name=\"1.19.2-1.18.0\"></a>\n## [1.19.2-1.18.0](/compare/1.19.2-1.17.0...1.19.2-1.18.0) - 2023-08-27 11:34:00 +0200\n\n\n### Added\n* Expose getChannelSlotted in positioned addons network\n  Required for CyclopsMC/IntegratedTunnels#271\n\n### Fixed\n* Fix rare crash when getting priority of moved network parts, Closes #1299\n* Fix crash when getting name of variable with ANY type, Closes #1301\n* Fix backspace not working in delayer GUI, Closes #1298\n\n<a name=\"1.19.2-1.17.0\"></a>\n## [1.19.2-1.17.0](/compare/1.19.2-1.16.10...1.19.2-1.17.0) - 2023-08-05 13:32:22 +0200\n\n\n### Added\n* Add generic variants of increment, decrement, and modulus\n  These were only usable for integers, but can now be used for all numbers.\n\n### Fixed\n* Fix wrong error message for invalid applied operators, Closes #1293\n\n<a name=\"1.19.2-1.16.10\"></a>\n## [1.19.2-1.16.10](/compare/1.19.2-1.16.9...1.19.2-1.16.10) - 2023-07-31 14:59:47 +0200\n\n\n### Fixed\n* Fix invisible light crash in debug worlds, Closes #1287\n\n<a name=\"1.19.2-1.16.9\"></a>\n## [1.19.2-1.16.9](/compare/1.19.2-1.16.8...1.19.2-1.16.9) - 2023-07-16 06:55:32 +0200\n\n\n### Changed\n* Give DOUBLE higher precedence than LONG for number operations, Closes #1284\n\n<a name=\"1.19.2-1.16.8\"></a>\n## [1.19.2-1.16.8](/compare/1.19.2-1.16.7...1.19.2-1.16.8) - 2023-07-08 14:47:21 +0200\n\n\n### Fixed\n* Fix part comparator depending on state\n  This could cause problems where chunks would be reloaded during unloading.\n  This issue was introduced when part offsets were added.\n  Closes #1257\n* Fix inability to remove text from recipes in LP, Closes #1280\n* Fix formatting error in operator cards tooltips, Closes #1281\n\n<a name=\"1.19.2-1.16.7\"></a>\n## [1.19.2-1.16.7](/compare/1.19.2-1.16.6...1.19.2-1.16.7) - 2023-06-04 08:15:49 +0200\n\n\n### Fixed\n* Fix broken Dynamic Light Panels, Closes #1276\n* Fix mechanical recipes incorrectly being validated\n  This fixes Integrated Crafting crafters not working on them.\n  Closes CyclopsMC/IntegratedDynamics#1275\n\n<a name=\"1.19.2-1.16.6\"></a>\n## [1.19.2-1.16.6](/compare/1.19.2-1.16.5...1.19.2-1.16.6) - 2023-05-23 18:10:38 +0200\n\n\n### Fixed\n* Fix double-shadow text printing in LP\n* Fix missing text colors in operator LP display\n* Fix Logic Programmer interface for lists of complex types\n  This makes it possible to makes lists of recipes, ingredients,\n  and operators.\n  Closes #641\n\n<a name=\"1.19.2-1.16.5\"></a>\n## [1.19.2-1.16.5](/compare/1.19.2-1.16.4...1.19.2-1.16.5) - 2023-05-16 17:46:17 +0200\n\n\n### Fixed\n* Fix offset render delay when crouching\n  Closes CyclopsMC/IntegratedTunnels#267\n\n<a name=\"1.19.2-1.16.4\"></a>\n## [1.19.2-1.16.4](/compare/1.19.2-1.16.3...1.19.2-1.16.4) - 2023-04-16 15:57:46 +0200\n\n\n### Changed\n* Set default part offset render distance to 16\n  This resolves performance issues due to too high render distances.\n  Existing worlds will have to manually change their configs for this change to apply (partOffsetRenderDistance).\n  Closes #1261\n\n<a name=\"1.19.2-1.16.3\"></a>\n## [1.19.2-1.16.3](/compare/1.19.2-1.16.2...1.19.2-1.16.3) - 2023-04-13 17:05:27 +0200\n\n\n### Fixed\n* Fix part enhancements dropping for offset 0, Closes #1259\n\n<a name=\"1.19.2-1.16.2\"></a>\n## [1.19.2-1.16.2](/compare/1.19.2-1.16.1...1.19.2-1.16.2) - 2023-03-19 08:02:54 +0100\n\n\n### Fixed\n* Fix delayer not propagating errors, Closes #1251\n* Fix delay error message not being shown in gui, Related to #1251\n\n<a name=\"1.19.2-1.16.1\"></a>\n## [1.19.2-1.16.1](/compare/1.19.2-1.16.0...1.19.2-1.16.1) - 2023-03-16 09:28:32 +0100\n\n\n### Added\n* Enable offsets in Redstone Writers\n\n<a name=\"1.19.2-1.16.0\"></a>\n## [1.19.2-1.16.0](/compare/1.19.2-1.15.2...1.19.2-1.16.0) - 2023-03-15 07:30:36 +0100\n\n\n### Added\n* Add support for static and dynamic offsets\n* Render offsets when holding wrench\n* Add modes to wrench to set offset and side of parts\n* Balance offsets using enhancement items\n\n<a name=\"1.19.2-1.15.2\"></a>\n## [1.19.2-1.15.2](/compare/1.19.2-1.15.1...1.19.2-1.15.2) - 2023-03-05 11:43:15 +0100\n\n\n### Changed\n* Use collapsed ingredient storage by default\n  Ingredient networks will now perform better for match-based lookups.\n  Related to CyclopsMC/IntegratedDynamics#1247\n\n<a name=\"1.19.2-1.15.1\"></a>\n## [1.19.2-1.15.1](/compare/1.19.2-1.15.0...1.19.2-1.15.1) - 2023-02-11 14:47:10 +0100\n\n\n### Fixed\n* Fix crash on filling recipes with JEI\n\n<a name=\"1.19.2-1.15.0\"></a>\n## [1.19.2-1.15.0](/compare/1.19.2-1.14.7...1.19.2-1.15.0) - 2023-02-11 13:46:18 +0100\n\n\n### Added\n* Allow recipe ingredients to be marked as reusable\n  Required for CyclopsMC/IntegratedCrafting#36\n\n### Fixed\n* Fix JEI recipe modification after modifying in LP\n  Closes CyclopsMC/IntegratedCrafting#89\n\n<a name=\"1.19.2-1.14.7\"></a>\n## [1.19.2-1.14.7](/compare/1.19.2-1.14.6...1.19.2-1.14.7) - 2023-01-21 07:23:29 +0100\n\n\n### Fixed\n* Fix JEI recipe modification after modifying in LP\n  Closes CyclopsMC/IntegratedCrafting#89\n\n<a name=\"1.19.2-1.14.6\"></a>\n## [1.19.2-1.14.6](/compare/1.19.2-1.14.5...1.19.2-1.14.6) - 2023-01-02 17:36:53 +0100\n\n\n### Fixed\n* Make invalid part containers not crash\n  This makes the game not crash anymore when frame-like mods are used.\n  Closes #1198\n\n<a name=\"1.19.2-1.14.5\"></a>\n## [1.19.2-1.14.5](/compare/1.19.2-1.14.4...1.19.2-1.14.5) - 2022-12-30 10:02:36 +0100\n\n\n### Fixed\n* Fix backspace not updating label in Logic Programmer, Closes #1239\n\n<a name=\"1.19.2-1.14.4\"></a>\n## [1.19.2-1.14.4](/compare/1.19.2-1.14.3...1.19.2-1.14.4) - 2022-12-16 11:14:35 +0100\n\nAs always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\n### Fixed\n* Fix corruption of mineable/pickaxe tag, Closes #1240\n\n\nThis is the same update as 1.14.3 to force CurseForge to update.\n\n<a name=\"1.19.2-1.14.3\"></a>\n## [1.19.2-1.14.3](/compare/1.19.2-1.14.2...1.19.2-1.14.3) - 2022-12-12 19:54:12 +0100\n\n\n### Fixed\n* Fix corruption of mineable/pickaxe tag, Closes #1240\n\n<a name=\"1.19.2-1.14.2\"></a>\n## [1.19.2-1.14.2](/compare/1.19.2-1.14.1...1.19.2-1.14.2) - 2022-12-11 13:51:22 +0100\n\n\n### Changed\n* Output 3 items when copying Omni-directional Connector, Closes #1238\n\n### Fixed\n* Fix some blocks being slow to break, Closes #1232\n* Fix incorrect unlocalized fluid names, Closes CyclopsMC/EvilCraft#950\n\n<a name=\"1.19.2-1.14.1\"></a>\n## [1.19.2-1.14.1](/compare/1.19.2-1.14.0...1.19.2-1.14.1) - 2022-11-27 15:55:59 +0100\n\n\n### Fixed\n* Fix crash when other mods shadow Netty\n\n<a name=\"1.19.2-1.14.0\"></a>\n## [1.19.2-1.14.0](/compare/1.19.2-1.13.1...1.19.2-1.14.0) - 2022-11-27 15:26:53 +0100\n\n\n### Added\n* Add Network Diagnostics tool for server operators, Closes #863\n  This restores the pre-1.15 diagnostics, but uses a Web browser to display it instead of the deprecated AWT libraries.\n\n<a name=\"1.19.2-1.13.1\"></a>\n## [1.19.2-1.13.1](/compare/1.19.2-1.13.0...1.19.2-1.13.1) - 2022-11-11 14:19:57 +0100\n\n\n### Changed\n* Cache slot counts of ingredient networks for a tick\n  This improves performance significantly for very large networks when they are being iterated over.\n  Closes #1229\n* Clarify meaning of consumption rate aspect, Closes #1216\n\n<a name=\"1.19.2-1.13.0\"></a>\n## [1.19.2-1.13.0](/compare/1.19.2-1.12.0...1.19.2-1.13.0) - 2022-10-26 10:40:00 +0200\n\n\n### Added\n* Add intersection operator\n* Add the forge:books tag to info book\n\n<a name=\"1.19.2-1.12.0\"></a>\n## [1.19.2-1.12.0](/compare/1.19.2-1.11.11...1.19.2-1.12.0) - 2022-10-08 15:04:27 +0200\n\n\n### Added\n* Add compact operator, for shortening numbers on displays, Closes #1217\n\n<a name=\"1.19.2-1.11.11\"></a>\n## [1.19.2-1.11.11](/compare/1.19.2-1.11.10...1.19.2-1.11.11) - 2022-09-17 12:19:33 +0200\n\n\n### Fixed\n* Fix omni-dir connector recipe not listed in infobook\n* Fix machine reader crash on drying basin, Closes #1212\n* Fix writer-based advancements for items triggering too soon, Closes CyclopsMC/IntegratedTunnels#258\n\n<a name=\"1.19.2-1.11.10\"></a>\n## [1.19.2-1.11.10](/compare/1.19.2-1.11.9...1.19.2-1.11.10) - 2022-09-02 16:57:38 +0200\n\n\n### Fixed\n* Fix crash on invalid characters in recipe tags, Closes #1209\n\n<a name=\"1.19.2-1.11.9\"></a>\n## [1.19.2-1.11.9](/compare/1.19.2-1.11.8...1.19.2-1.11.9) - 2022-08-29 17:51:23 +0200\n\n\n### Changed\n* Rename 'Crystalized' to 'Crystallized', Closes #1206\n\n### Fixed\n* Fix NBT reading of empty itemstacks\n  This fixes read NBT data not updating after non-player item move.\n  When things like hoppers drain items from inventories,\n  the stack size is set to zero, but the NBT tag is still\n  present on that item.\n  Because of that, we should only allow NBT reading of non-empty items\n  Closes #1208\n\n<a name=\"1.19.2-1.11.8\"></a>\n## [1.19.2-1.11.8](/compare/1.19.2-1.11.7...1.19.2-1.11.8) - 2022-08-13 06:56:16 +0200\n\n\n### Fixed\n* Improve description of speachMaxFrequency property\n\n<a name=\"1.19.2-1.11.7\"></a>\n## [1.19.2-1.11.7] - 2022-08-11 19:58:03 +0200\n\n\nUpdate to MC 1.19.2\n\n### Fixed\n* Fix machine gui crash upon client desync on fluid data, Closes #1197\n* Fix squeezers unable to handle deepslate coal ore, Closes #1199\n"
  },
  {
    "path": "CHANGELOG-1.20.1.md",
    "content": "# Changelog for Minecraft 1.20.1\nAll notable changes to this project will be documented in this file.\n\n<a name=\"1.20.1-1.30.5\"></a>\n## [1.20.1-1.30.5](/compare/1.20.1-1.30.4...1.20.1-1.30.5) - 2026-04-28 19:37:09\n\n\n### Fixed\n* Fix incorrect position of channel disabled tooltip in Integrated Crafting\n\n<a name=\"1.20.1-1.30.4\"></a>\n## [1.20.1-1.30.4](/compare/1.20.1-1.30.3...1.20.1-1.30.4) - 2026-04-18 15:29:21 +0200\n\n\n### Changed\n* Add BlockCable#getAppearance for connected textures on facades\n  This adds basic support to facades on cables for mods that add connected texture support.\n\n<a name=\"1.20.1-1.30.3\"></a>\n## [1.20.1-1.30.3](/compare/1.20.1-1.30.2...1.20.1-1.30.3) - 2026-04-11 19:53:57 +0200\n\n\n### Changed\n* Give Delayer its own energy consumption config separate from Proxy (#1641), Closes #1640\n\n<a name=\"1.20.1-1.30.2\"></a>\n## [1.20.1-1.30.2](/compare/1.20.1-1.30.1...1.20.1-1.30.2) - 2026-03-28 09:55:51 +0100\n\n\n### Added\n* Add configurable energy production rate for coal generator (#1635), Closes #1634\n\n### Fixed\n* Fix Variable Store, Proxy, Materializer, and Static Light Panel not consuming energy (#1633), Closes #1632\n* Fix energy battery texture overflow after NBT manipulation, Closes #1636 (#1637)\n\n<a name=\"1.20.1-1.30.1\"></a>\n## [1.20.1-1.30.1](/compare/1.20.1-1.30.0...1.20.1-1.30.1) - 2026-03-20 20:03:22 +0100\n\n\n### Fixed\n* Fix Mono & Omni Directional connectors not consuming energy (#1631), Closes #1630\n\n<a name=\"1.20.1-1.30.0\"></a>\n## [1.20.1-1.30.0](/compare/1.20.1-1.29.11...1.20.1-1.30.0) - 2026-02-17 10:58:20 +0100\n\n\n### Added\n* Document logical NBT expressions, CyclopsMC/CyclopsCore#210\n* Add config options for minimum update intervals for parts, Closes CyclopsMC/IntegratedTunnels#349\n* Add config option to log card IDs during evaluation, Closes #1345\n\n<a name=\"1.20.1-1.29.11\"></a>\n## [1.20.1-1.29.11](/compare/1.20.1-1.29.10...1.20.1-1.29.11) - 2026-01-17 14:05:34 +0100\n\n\n### Added\n* Add generic number casting operators\n  Closes CyclopsMC/IntegratedScripting#55\n  These new operators make it more convenient to work with return types of\n  Integrated Scripting, where numbers could be integers, doubles, or\n  longs.\n\n### Changed\n* Add safety check for infinite lists in list equals and itemstack count\n  Closes #1583\n  Related to #1582\n\n### Fixed\n* Fix part states not being revalidated, Closes #1588\n* Fix oldest/newest mixup on delayer history in book, Closes #1592\n* Fix cable shape cache having cache collisions when there are two entries that have the same part on two different sides while having the same cable connections\n\n<a name=\"1.20.1-1.29.10\"></a>\n## [1.20.1-1.29.10](/compare/1.20.1-1.29.9...1.20.1-1.29.10) - 2026-01-02 10:49:29 +0100\n\n\n### Changed\n* Add IIngredientChannelInsertPreConsumer\n  Required for CyclopsMC/IntegratedCrafting#170\n\n<a name=\"1.20.1-1.29.9\"></a>\n## [1.20.1-1.29.9](/compare/1.20.1-1.29.8...1.20.1-1.29.9) - 2025-12-31 14:34:39 +0100\n\n\n### Changed\n* Keep insertion order of network change observers\n  Required for CyclopsMC/IntegratedCrafting#112\n\n<a name=\"1.20.1-1.29.8\"></a>\n## [1.20.1-1.29.8](/compare/1.20.1-1.29.7...1.20.1-1.29.8) - 2025-12-28 09:17:57 +0100\n\n\n### Fixed\n* Add safety checks to disable some operators on infinite lists\n  Concretely, contains, reduce, and uniq are disabled on infinite lists.\n  Closes #1582\n\n<a name=\"1.20.1-1.29.7\"></a>\n## [1.20.1-1.29.7](/compare/1.20.1-1.29.6...1.20.1-1.29.7) - 2025-12-23 09:16:36 +0100\n\n\n### Added\n* Add squeezer recipes for clearing facades\n\n<a name=\"1.20.1-1.29.6\"></a>\n## [1.20.1-1.29.6](/compare/1.20.1-1.29.5...1.20.1-1.29.6) - 2025-11-25 16:58:55 +0100\n\n\n### Fixed\n* Restore network element chunk unloading again, Closes #1571\n\n<a name=\"1.20.1-1.29.5\"></a>\n## [1.20.1-1.29.5](/compare/1.20.1-1.29.4...1.20.1-1.29.5) - 2025-11-24 16:57:21 +0100\n\n\n### Fixed\n* Temporarily disable chunk unload check again\n\nIt is causing issues with delayers after chunk reloaded.\nOnce a proper fix is available, this will be re-enabled.\n\nRelated to #1571\n\n<a name=\"1.20.1-1.29.4\"></a>\n## [1.20.1-1.29.4](/compare/1.20.1-1.29.3...1.20.1-1.29.4) - 2025-11-22 11:26:51 +0100\n\n\n### Fixed\n* Restore network element chunk unloading\n  This was temporarily disabled as it broke delayers and proxies on\n  world/chunk reload, which this commit fixes.\n  Closes #1571\n\n<a name=\"1.20.1-1.29.3\"></a>\n## [1.20.1-1.29.3](/compare/1.20.1-1.29.2...1.20.1-1.29.3) - 2025-11-18 18:47:04 +0100\n\n\n### Fixed\n* Temporarily disable chunk unload check\n  It is causing issues with delayers when worlds/chunks are reloaded.\n  Once a proper fix is available, this will be re-enabled.\n  Related to #1571\n\n<a name=\"1.20.1-1.29.2\"></a>\n## [1.20.1-1.29.2](/compare/1.20.1-1.29.1...1.20.1-1.29.2) - 2025-11-18 05:32:15 +0100\n\n\n### Fixed\n* Fix read fluids not always updating in display panel, Closes #1570\n\n<a name=\"1.20.1-1.29.1\"></a>\n## [1.20.1-1.29.1](/compare/1.20.1-1.29.0...1.20.1-1.29.1) - 2025-11-16 14:30:49 +0100\n\n\n### Fixed\n* Fix ticking network elements preventing chunk unloading, Closes #1567\n\n<a name=\"1.20.1-1.29.0\"></a>\n## [1.20.1-1.29.0](/compare/1.20.1-1.28.1...1.20.1-1.29.0) - 2025-11-11 13:56:49 +0100\n\n\n### Added\n* Add fluid by name operator, Closes #1522\n\n### Changed\n* Ensure consistent order of ingredient types in LP, Closes #1539\n\n### Fixed\n* Fix transparent facades having incorrect alpha value, Closes #1563\n* Fix lists materialization not applying to elements\n  This could cause issues when displaying lists of items from inventory\n  readers over Functional Storage Drawers.\n  Closes #1557\n\n<a name=\"1.20.1-1.28.1\"></a>\n## [1.20.1-1.28.1](/compare/1.20.1-1.28.0...1.20.1-1.28.1) - 2025-10-11 11:49:46 +0200\n\n\n### Changed\n* Improve offset gui error tooltip\n\n### Fixed\n* Fix memory leak when reloading offset variables, Closes #1549\n* Handle overflows in fluid reader total aspects, Closes #1547\n\n<a name=\"1.20.1-1.28.0\"></a>\n## [1.20.1-1.28.0](/compare/1.20.1-1.27.6...1.20.1-1.28.0) - 2025-10-07 07:36:20 +0200\n\n\n### Added\n* Add config option to disable cable collisions(#1538)\n\n### Fixed\n* Properly handle long overflows in channel quantities\n\nInternal changes (required for Integrated Mekanism):\n* Support regexes in operators_output appendix\n* Abstract parts of recipe LP element\n* Make CommandTest more extensible\n* Make API less dependent on ModBase\n\n<a name=\"1.20.1-1.27.6\"></a>\n## [1.20.1-1.27.6](/compare/1.20.1-1.27.5...1.20.1-1.27.6) - 2025-07-18 20:36:55 +0200\n\n\n### Fixed\n* Fix crash on load operator variable in LP, Closes #1537\n\n<a name=\"1.20.1-1.27.5\"></a>\n## [1.20.1-1.27.5](/compare/1.20.1-1.27.4...1.20.1-1.27.5) - 2025-07-05 06:55:13 +0200\n\n\n### Fixed\n* Fix LP crash when loading with empty category card, Closes #1527\n\n<a name=\"1.20.1-1.27.4\"></a>\n## [1.20.1-1.27.4](/compare/1.20.1-1.27.3...1.20.1-1.27.4) - 2025-06-21 13:22:30 +0200\n\n\n### Changed\n* Return item to player instead of dropping on Labeler exit, Closes #1526\n\n### Fixed\n* Fix fluidstack operator crash on empty items, Closes #1525\n\n<a name=\"1.20.1-1.27.3\"></a>\n## [1.20.1-1.27.3](/compare/1.20.1-1.27.2...1.20.1-1.27.3) - 2025-06-12 16:36:45 +0200\n\n\n### Fixed\n* Fix cables not being breakable with wrench, Closes #1523\n\n<a name=\"1.20.1-1.27.2\"></a>\n## [1.20.1-1.27.2](/compare/1.20.1-1.27.1...1.20.1-1.27.2) - 2025-06-07 18:02:35 +0200\n\n\n### Changed\n* Only switch Wrench mode when aiming at air\n  Closes #1518\n  Closes #1390\n\n### Fixed\n* Fix overlapping tooltips in LP write slot, Closes #1519\n\n<a name=\"1.20.1-1.27.1\"></a>\n## [1.20.1-1.27.1](/compare/1.20.1-1.27.0...1.20.1-1.27.1) - 2025-05-31 20:28:21 +0200\n\n\n### Fixed\n* Fix missing variable by id aspect texture\n\n<a name=\"1.20.1-1.27.0\"></a>\n## [1.20.1-1.27.0](/compare/1.20.1-1.26.1...1.20.1-1.27.0) - 2025-05-31 19:50:43 +0200\n\n\n### Added\n* Allow editing existing variables in Logic Programmer, Closes #357\n* Add Variable Value By ID operator aspect to network reader, Closes #1346\n* Add button to reset selected LP element\n\n### Changed\n* Don't drop item in world when closing LP\n* Improve LP element arrow\n\n### Fixed\n* Fix operator text field in LP sometimes being red\n\n<a name=\"1.20.1-1.26.1\"></a>\n## [1.20.1-1.26.1](/compare/1.20.1-1.26.0...1.20.1-1.26.1) - 2025-05-20 17:30:17 +0200\n\n\n### Fixed\n* Fix search hotkey also typing in hotkey in search box\n  Closes CyclopsMC/IntegratedTerminals#168\n\n<a name=\"1.20.1-1.26.0\"></a>\n## [1.20.1-1.26.0](/compare/1.20.1-1.25.6...1.20.1-1.26.0) - 2025-05-03 15:44:41 +0200\n\n\n### Added\n* Add list set and multiset equality operators, Closes #1347\n* Add Fluid with tag operator, Closes #797\n* Add block and fluid tag operators, Closes #1394\n* Add Sqrt and Pow operators, Closes #1372\n* Add lectern and bookshelf support for infobook, Closes #1496\n\n### Fixed\n* Fix non-fluid items being insertable into LP, Closes #1507\n\n<a name=\"1.20.1-1.25.6\"></a>\n## [1.20.1-1.25.6](/compare/1.20.1-1.25.5...1.20.1-1.25.6) - 2025-03-22 15:13:34 +0100\n\n\n### Fixed\n* Fix NBT from int list not working for any lists\n  Closes CyclopsMC/IntegratedScripting#37\n* Fix typos in manual\n* Fixed number typo\n* Fixed variable description\n* Fixed typo in reader introduction\n\n<a name=\"1.20.1-1.25.5\"></a>\n## [1.20.1-1.25.5](/compare/1.20.1-1.25.4...1.20.1-1.25.5) - 2025-03-10 06:57:43 +0100\n\n\n### Changed\n* Avoid repeated log spam for forcefully unloaded parts, Closes #1481\n\n<a name=\"1.20.1-1.25.4\"></a>\n## [1.20.1-1.25.4](/compare/1.20.1-1.25.3...1.20.1-1.25.4) - 2025-03-01 07:06:30 +0100\n\n\n### Changed\n* Improve performance of block changes to very large networks\n  Pathfinder is ~70x faster, also improved tick speed with lots of devices (17 ms to 3 ms in test scene)\n\n### Fixed\n* Fix duplication of offset enchancements when breaking cable, Closes #1480\n* Fix passive interaction changes not immediately applying on change\n  It used to only take effect after re-inserting a variable card.\n  Closes CyclopsMC/IntegratedDynamics#1470\n\n<a name=\"1.20.1-1.25.3\"></a>\n## [1.20.1-1.25.3](/compare/1.20.1-1.25.2...1.20.1-1.25.3) - 2025-02-22 17:04:39 +0100\n\n\n### Fixed\n* Fix GUIs remaining open after external breakage, Closes #1472\n* Fix rare tooltip crash for item-based variable cards, Closes #1477\n\n<a name=\"1.20.1-1.25.2\"></a>\n## [1.20.1-1.25.2](/compare/1.20.1-1.25.1...1.20.1-1.25.2) - 2025-02-08 15:59:04 +0100\n\n\n### Fixed\n* Fix LP recipe slot properties not displaying tag tooltip items\n\n<a name=\"1.20.1-1.25.1\"></a>\n## [1.20.1-1.25.1](/compare/1.20.1-1.25.0...1.20.1-1.25.1) - 2025-02-03 16:59:28 +0100\n\n\n### Added\n* Add Item with tag operation\n\n### Fixed\n* Fix REI recipe transfer to LP not working for fluids\n* Fix LP recipe transfer including chance-based outputs\n  Non-1000utputs could cause issues for autocrafting.\n  This is fixed for both JEI and REI.\n  Closes CyclopsMC/IntegratedCrafting#127\n* Fix tag-based JEI recipe transfer ignoring stack sizes\n  Closes CyclopsMC/IntegratedCrafting#128\n* Fix placing part before cable not properly connecting cables\n  Closes CyclopsMC/IntegratedTunnels#321\n\n<a name=\"1.20.1-1.25.0\"></a>\n## [1.20.1-1.25.0](/compare/1.20.1-1.24.3...1.20.1-1.25.0) - 2025-01-08 17:34:03 +0100\n\n\n### Added\n* Add item tooltip operators\n\n<a name=\"1.20.1-1.24.3\"></a>\n## [1.20.1-1.24.3](/compare/1.20.1-1.24.2...1.20.1-1.24.3) - 2024-12-28 13:57:28 +0100\n\n\n### Added\n* Add lossy recipe to clear a facade in a crafting grid, Closes #1424\n\n### Fixed\n* Fix client-server desync when applying part offsets, Closes #1448\n* Fix offset items only being applicable to 28 instead of 32, #1448\n\n<a name=\"1.20.1-1.24.2\"></a>\n## [1.20.1-1.24.2](/compare/1.20.1-1.24.1...1.20.1-1.24.2) - 2024-12-24 09:53:13 +0100\n\n\n### Fixed\n* Reduce number of network events during init to improve performance, #1439\n  Modifying very large networks is a lot faster now.\n\n<a name=\"1.20.1-1.24.1\"></a>\n## [1.20.1-1.24.1](/compare/1.20.1-1.24.0...1.20.1-1.24.1) - 2024-12-17 11:10:55 +0100\n\n\n### Added\n* Add error operator\n\n### Changed\n* Reduce network inits by half when placing cables, Closes #1439\n\n<a name=\"1.20.1-1.24.0\"></a>\n## [1.20.1-1.24.0](/compare/1.20.1-1.23.13...1.20.1-1.24.0) - 2024-12-06 16:00:24 +0100\n\n\n### Added\n* Add dedicated REI support, Closes CyclopsMC/IntegratedDynamics#1348\n* Restore Refined Storage integration\n* Restore Jade/Waila integration, Closes CyclopsMC/IntegratedDynamics#1413\n\n### Fixed\n* Fix cable placement with commands initializing networks\n  This was broken since CyclopsMC/IntegratedTunnels#243\n\n<a name=\"1.20.1-1.23.13\"></a>\n## [1.20.1-1.23.13](/compare/1.20.1-1.23.12...1.20.1-1.23.13) - 2024-11-19 15:17:34 +0100\n\n\n### Changed\n* Drop Part Offsets into their original form, Closes #1418\n\n### Fixed\n* Fix creative batteries not providing energy, Closes #1421\n\n<a name=\"1.20.1-1.23.12\"></a>\n## [1.20.1-1.23.12](/compare/1.20.1-1.23.11...1.20.1-1.23.12) - 2024-11-10 14:12:22 +0100\n\n\n### Fixed\n* Fix variables dependent on offset aspects not always updating, Closes #1416\n\n<a name=\"1.20.1-1.23.11\"></a>\n## [1.20.1-1.23.11](/compare/1.20.1-1.23.10...1.20.1-1.23.11) - 2024-11-02 16:01:17 +0100\n\n\n### Changed\n* Optimize ingredient positions index lookups\n  Closes CyclopsMC/IntegratedTunnels#307\n\n### Fixed\n* Fix Deepslate Dark Ore not being squeezable, Closes #1414\n\n<a name=\"1.20.1-1.23.10\"></a>\n## [1.20.1-1.23.10](/compare/1.20.1-1.23.9...1.20.1-1.23.10) - 2024-10-30 18:33:31 +0100\n\n\n### Fixed\n* Fix crash with complex Integrated Scripting functions in writers\n\nFunctions returning an any type could be inserted into writers such as\nIntegrated Tunnels exporters, and type checking would incorrectly pass.\nThis could result in crashes where an incorrect value cast would occur.\nThis commit makes it so that ANY types will have an additional type\ncheck based on the actual determined value.\n\nCloses CyclopsMC/IntegratedScripting#20\n\n<a name=\"1.20.1-1.23.9\"></a>\n## [1.20.1-1.23.9](/compare/1.20.1-1.23.8...1.20.1-1.23.9) - 2024-10-30 17:03:11 +0100\n\n\n### Fixed\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 8f53b7eb53302e5028d1b4414caf288d26eb73ee.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n<a name=\"1.20.1-1.23.8\"></a>\n## [1.20.1-1.23.8](/compare/1.20.1-1.23.7...1.20.1-1.23.8) - 2024-10-29 18:50:32 +0100\n\n\n### Fixed\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 61b7372bda5cde123a2b320ac92c1c24d2dfb9b1.\n  Closes CyclopsMC/IntegratedTerminals#134\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n\n<a name=\"1.20.1-1.23.7\"></a>\n## [1.20.1-1.23.7](/compare/1.20.1-1.23.6...1.20.1-1.23.7) - 2024-10-28 17:30:11 +0100\n\n\n### Changed\n* Optimize ingredient positions index\n  This improves performance with Integrated Tunnels and Terminals.\n\n<a name=\"1.20.1-1.23.6\"></a>\n## [1.20.1-1.23.6](/compare/1.20.1-1.23.5...1.20.1-1.23.6) - 2024-10-24 16:52:56 +0200\n\n\n### Fixed\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n\n<a name=\"1.20.1-1.23.5\"></a>\n## [1.20.1-1.23.5](/compare/1.20.1-1.23.4...1.20.1-1.23.5) - 2024-10-14 15:17:30 +0200\n\n\n### Fixed\n* Fix broken biome aspect in block reader, Closes #1403\n* Fix incorrect type checking in complex reduce operation, Closes #1387\n\n<a name=\"1.20.1-1.23.4\"></a>\n## [1.20.1-1.23.4](/compare/1.20.1-1.23.3...1.20.1-1.23.4) - 2024-10-03 19:06:24 +0200\n\n\n### Fixed\n* Fix JEI ghosts items not working for lists in the LP, Closes CyclopsMC/IntegratedDynamics#1398\n* Fix wrong sided part being shown in The One Probe, Closes #1401\n* Fix round-robin misbehaving with filtered interfaces, Closes CyclopsMC/IntegratedTunnels#302\n\n<a name=\"1.20.1-1.23.3\"></a>\n## [1.20.1-1.23.3](/compare/1.20.1-1.23.2...1.20.1-1.23.3) - 2024-09-02 18:03:34 +0200\n\n\n### Fixed\n* Delay Terrablender registration\n  This fixes rare crashes when Terrablender was not yet fully initialized.\n  CyclopsMC/IntegratedDynamics#1385\n  Closes CyclopsMC/IntegratedDynamics#1388\n\n<a name=\"1.20.1-1.23.2\"></a>\n## [1.20.1-1.23.2](/compare/1.20.1-1.23.1...1.20.1-1.23.2) - 2024-08-22 18:58:35 +0200\n\n\n### Fixed\n* Fix op_by_name crashing if ResourceLocation is invalid, Closes #1381\n* Fix regex scan producing illegal lists for non-zero groups, Closes #1378\n\n<a name=\"1.20.1-1.23.1\"></a>\n## [1.20.1-1.23.1](/compare/1.20.1-1.23.0...1.20.1-1.23.1) - 2024-08-01 08:12:14 +0200\n\n\n### Fixed\n* Fixed Feature Order Cycle (FOC)\n\n<a name=\"1.20.1-1.23.0\"></a>\n## [1.20.1-1.23.0](/compare/1.20.1-1.22.2...1.20.1-1.23.0) - 2024-07-31 15:15:43 +0200\n\n\n### Added\n* Add Terrablender compat for Meneglin biome\n\n### Fixed\n* Fix menril trees not spawning in menril biome\n\n<a name=\"1.20.1-1.22.2\"></a>\n## [1.20.1-1.22.2](/compare/1.20.1-1.22.1...1.20.1-1.22.2) - 2024-07-21 11:39:18 +0200\n\n\n### Fixed\n* Interrupt speech before sending a new one to the narrator, Closes #1356\n\n<a name=\"1.20.1-1.22.1\"></a>\n## [1.20.1-1.22.1](/compare/1.20.1-1.22.0...1.20.1-1.22.1) - 2024-06-24 10:19:59 +0200\n\n\n### Changed\n* Also copy label when copying variable cards, Closes #1354\n* Allow Ingredient.with* operators to have empty ingredient inputs\n* Allow entering items in logic programmer with larger stack sizes\n\n### Fixed\n* Fix default labeller textfield being modifiable, Closes #1352\n* Fix apply_0 crashing on operators with input, Closes CyclopsMC/IntegratedScripting#11\n* Fix incorrect comparator in PrioritizedPartPos\n\n<a name=\"1.20.1-1.22.0\"></a>\n## [1.20.1-1.22.0](/compare/1.20.1-1.21.3...1.20.1-1.22.0) - 2024-04-28 10:05:59 +0200\n\n\n### Added\n* Allow checking if there are variable-based offsets in API\n  Required for CyclopsMC/IntegratedTunnels#289\n\n<a name=\"1.20.1-1.21.3\"></a>\n## [1.20.1-1.21.3](/compare/1.20.1-1.21.2...1.20.1-1.21.3) - 2024-04-14 14:05:13 +0200\n\n\n### Changed\n* Allow listening to EvaluationException resolutions, Required for CyclopsMC/IntegratedScripting#5\n\n### Fixed\n* Fix crash for illegal item tag chars in recipe in LP, Closes CyclopsMC/IntegratedCrafting#103\n* Fix wrong ingredient slot positions in LP, Closes #1336\n* Fix Dank Storage item removal when placing cables, Closes #1332\n* Fix facades being craftable for non-solid blocks, Closes #1334, #1342\n\n<a name=\"1.20.1-1.21.2\"></a>\n## [1.20.1-1.21.2](/compare/1.20.1-1.21.1...1.20.1-1.21.2) - 2024-02-11 17:02:17 +0100\n\n\n### Fixed\n* Fix tps and ticktime aspects returning values different to Forge, Closes #1325\n\n<a name=\"1.20.1-1.21.1\"></a>\n## [1.20.1-1.21.1](/compare/1.20.1-1.21.0...1.20.1-1.21.1) - 2024-02-07 19:16:36 +0100\n\n\n### Fixed\n* Fix part offsets sometimes being delayed by one aspect update, Closes #1320\n* Fix errored offset variables not resetting after network reset, Closes #1321\n* Fix facade not being placeable on readers, Closes #1324\n\n<a name=\"1.20.1-1.21.0\"></a>\n## [1.20.1-1.21.0](/compare/1.20.1-1.20.4...1.20.1-1.21.0) - 2024-02-04 16:05:09 +0100\n\n\n### Added\n* Expose interact names for operators\n  This is required for Integrated Scripting\n* Add apply_0 operator\n* Add apply_n operator\n\n### Changed\n* Add INetwork parameter to variable facade methods\n\n### Fixed\n* Fix duplicate variable invalidate listeners being registered\n  This could leak to memory leaks for long-running multi-arg operators.\n\n<a name=\"1.20.1-1.20.4\"></a>\n## [1.20.1-1.20.4](/compare/1.20.1-1.20.3...1.20.1-1.20.4) - 2023-12-27 17:23:47 +0100\n\n\n### Fixed\n* Fix hard crash on replace_regex with invalid group, Closes #1317\n\n<a name=\"1.20.1-1.20.3\"></a>\n## [1.20.1-1.20.3](/compare/1.20.1-1.20.2...1.20.1-1.20.3) - 2023-12-04 11:06:21 +0100\n\n\n### Fixed\n* Fix predicate-based filtering interfaces causing ingredient loss\n  Integrated Tunnels filtering interaces could lose items\n  when using predicate-based filters.\n  Closes CyclopsMC/IntegratedTunnels#282\n\n<a name=\"1.20.1-1.20.2\"></a>\n## [1.20.1-1.20.2](/compare/1.20.1-1.20.1...1.20.1-1.20.2) - 2023-11-27 15:52:59 +0100\n\n\n### Fixed\n* Fix NBT.from_tag_list not working on Any lists, Closes #1315\n\n<a name=\"1.20.1-1.20.1\"></a>\n## [1.20.1-1.20.1](/compare/1.20.1-1.20.0...1.20.1-1.20.1) - 2023-11-02 15:31:03 +0100\n\n\n### Fixed\n* Fix inputs of JEI recipes not being recognized\n  Closes CyclopsMC/IntegratedDynamics#1312\n\n<a name=\"1.20.1-1.20.0\"></a>\n## [1.20.1-1.20.0](/compare/1.20.1-1.19.1...1.20.1-1.20.0) - 2023-10-10 16:55:56 +0200\n\n\n### Added\n* Add initialChange flag to storage change events\n  This is required to fix CyclopsMC/IntegratedCrafting#99\n\n<a name=\"1.20.1-1.19.1\"></a>\n## [1.20.1-1.19.1](/compare/1.20.1-1.19.0...1.20.1-1.19.1) - 2023-09-24 11:42:53 +0200\n\n\n### Fixed\n* Fix held items vanishing after gui close, Closes #1308\n\n<a name=\"1.20.1-1.19.0\"></a>\n## [1.20.1-1.19.0](/compare/1.20.1-1.18.0...1.20.1-1.19.0) - 2023-09-12 19:51:13 +0200\n\n\n### Added\n* Expose positions from channels\n  This is required for CyclopsMC/IntegratedCrafting#98\n\n### Fixed\n* Fix predicate-based tunnels movement ignoring channels, Closes CyclopsMC/IntegratedTunnels#274\n* Fix crash on special operator with ANY type, Closes #1301\n* Fix Menril Tree replacing grass with dirt on stump place fail, Closes #1304\n\n<a name=\"1.20.1-1.18.0\"></a>\n## [1.20.1-1.18.0](/compare/1.20.1-1.17.0...1.20.1-1.18.0) - 2023-08-27 11:42:39 +0200\n\n\n### Added\n* Expose getChannelSlotted in positioned addons network\n  Required for CyclopsMC/IntegratedTunnels#271\n\n### Fixed\n* Fix rare crash when getting priority of moved network parts, Closes #1299\n* Fix crash when getting name of variable with ANY type, Closes #1301\n* Fix labeling text overlap in LP, Closes #1297\n* Fix backspace not working in delayer GUI, Closes #1298\n* Fix rare crash when loading positioned storages on high server load, Closes #1302\n\n<a name=\"1.20.1-1.17.0\"></a>\n## [1.20.1-1.17.0](/compare/1.20.1-1.16.10...1.20.1-1.17.0) - 2023-08-05 13:34:49 +0200\n\n\n### Added\n* Add generic variants of increment, decrement, and modulus\n  These were only usable for integers, but can now be used for all numbers.\n\n### Fixed\n* Fix wrong error message for invalid applied operators, Closes #1293\n\n<a name=\"1.20.1-1.16.10\"></a>\n## [1.20.1-1.16.10](/compare/1.20.1-1.16.9...1.20.1-1.16.10) - 2023-07-31 15:02:03 +0200\n\n\n### Fixed\n* Fix some logic programmer inputs being rendered incorrectly, Closes #1289\n* Fix invisible light crash in debug worlds, Closes #1287\n\n<a name=\"1.20.1-1.16.9\"></a>\n## [1.20.1-1.16.9](/compare/1.20.1-1.16.8...1.20.1-1.16.9) - 2023-07-16 06:57:51 +0200\n\n\n### Changed\n* Give DOUBLE higher precedence than LONG for number operations, Closes #1284\n\n<a name=\"1.20.1-1.16.8\"></a>\n## [1.20.1-1.16.8](/compare/1.20.1-1.16.7...1.20.1-1.16.8) - 2023-07-08 14:49:12 +0200\n\n\n### Fixed\n* Fix random crash when placing squeezer, Closes #1282\n* Fix part comparator depending on state\n  This could cause problems where chunks would be reloaded during unloading.\n  This issue was introduced when part offsets were added.\n  Closes #1257\n* Fix focus between LP elements not being swapped correctly\n* Fix inability to remove text from recipes in LP, Closes #1280\n* Fix formatting error in operator cards tooltips, Closes #1281\n\n<a name=\"1.20.1-1.16.7\"></a>\n## [1.20.1-1.16.7] - 2023-07-02 08:11:10 +0200\n\n\nInitial 1.20.1 release\n"
  },
  {
    "path": "CHANGELOG-1.21.1.md",
    "content": "# Changelog for Minecraft 1.21.1\nAll notable changes to this project will be documented in this file.\n\n<a name=\"1.21.1-1.32.6\"></a>\n## [1.21.1-1.32.6](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.32.5...1.21.1-1.32.6) - 2026-04-28 19:39:17\n\n\n### Fixed\n* Fix incorrect position of channel disabled tooltip in Integrated Crafting\n\n<a name=\"1.21.1-1.32.5\"></a>\n## [1.21.1-1.32.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.32.4...1.21.1-1.32.5) - 2026-04-18 15:33:04 +0200\n\n\n### Changed\n* Add BlockCable#getAppearance for connected textures on facades\n  This adds basic support to facades on cables for mods that add connected texture support.\n\n<a name=\"1.21.1-1.32.4\"></a>\n## [1.21.1-1.32.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.32.3...1.21.1-1.32.4) - 2026-04-11 19:55:58 +0200\n\n\n### Changed\n* Give Delayer its own energy consumption config separate from Proxy (#1641), Closes #1640\n\n### Fixed\n* Fix constant list Variable Card deserialization failure after world reload (#1639), Closes #1639\n\n<a name=\"1.21.1-1.32.3\"></a>\n## [1.21.1-1.32.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.32.2...1.21.1-1.32.3) - 2026-03-28 10:18:26 +0100\n\n\n### Added\n* Add configurable energy production rate for coal generator (#1635), Closes #1634\n\n### Fixed\n* Fix Variable Store, Proxy, Materializer, and Static Light Panel not consuming energy (#1633), Closes #1632\n* Fix energy battery texture overflow after NBT manipulation, Closes #1636 (#1637)\n\n<a name=\"1.21.1-1.32.2\"></a>\n## [1.21.1-1.32.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.32.1...1.21.1-1.32.2) - 2026-03-20 20:04:50 +0100\n\n\n### Fixed\n* Fix Mono & Omni Directional connectors not consuming energy (#1631), Closes #1630\n\n<a name=\"1.21.1-1.32.1\"></a>\n## [1.21.1-1.32.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.32.0...1.21.1-1.32.1) - 2026-03-09 16:15:13 +0100\n\n\n### Fixed\n* Fix ClassCastException in BlockCable when sapling grows nearby (#1623), Closes #1622\n* Fix tool_for_obsidian advancement triggering too often (#1620), Related to #1606\n* Fix VariablePredicate skipping getValue() when no value predicate is set (#1619)\n  Required for fixing game tests in CyclopsMC/IntegratedTunnels#354\n* Add game tests for all advancement criteria and fix broken advancements (#1617)\n\n<a name=\"1.21.1-1.32.0\"></a>\n## [1.21.1-1.32.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.31.0...1.21.1-1.32.0) - 2026-02-22 14:09:45 +0100\n\n\n### Added\n* Add config option to force unloaded network elements to tick, Related to #1605, #1571, #1567\n* Add command for generating synthetic networks\n* Add command to measure network tick time\n\n### Fixed\n* Fix parts sometimes becoming invalid, Closes #1605\n\n<a name=\"1.21.1-1.31.0\"></a>\n## [1.21.1-1.31.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.30.5...1.21.1-1.31.0) - 2026-02-17 10:58:49 +0100\n\n\n### Added\n* Add configurable pulse length property to Redstone Writer, Closes #724 (#1602)\n* Document logical NBT expressions, CyclopsMC/CyclopsCore#210\n* Add config options for minimum update intervals for parts, Closes CyclopsMC/IntegratedTunnels#349\n* Add config option to log card IDs during evaluation, Closes #1345\n* Add translations through Crowdin (#1598)\n\n<a name=\"1.21.1-1.30.5\"></a>\n## [1.21.1-1.30.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.30.4...1.21.1-1.30.5) - 2026-01-24 15:18:33 +0100\n\n\n### Added\n* Add translations through Crowdin\n\n### Fixed\n* Fix missing capability invalidation on modified parts, #1594\n\n<a name=\"1.21.1-1.30.4\"></a>\n## [1.21.1-1.30.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.30.3...1.21.1-1.30.4) - 2026-01-17 14:10:56 +0100\n\n\n### Added\n* Add Almost Unified integration\n* Add generic number casting operators\n  Closes CyclopsMC/IntegratedScripting#55\n  These new operators make it more convenient to work with return types of\n  Integrated Scripting, where numbers could be integers, doubles, or\n  longs.\n\n### Changed\n* Add safety check for infinite lists in list equals and itemstack count\n  Closes #1583\n  Related to #1582\n\n### Fixed\n* Fix part states not being revalidated, Closes #1588\n* Fix oldest/newest mixup on delayer history in book, Closes #1592\n* Fix cable shape cache having cache collisions when there are two entries that have the same part on two different sides while having the same cable connections\n\n<a name=\"1.21.1-1.30.3\"></a>\n## [1.21.1-1.30.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.30.2...1.21.1-1.30.3) - 2026-01-02 10:52:15 +0100\n\n\n### Added\n* Add translations through Crowdin (#1581)\n\n### Changed\n* Add IIngredientChannelInsertPreConsumer\n  Required for CyclopsMC/IntegratedCrafting#170\n\n<a name=\"1.21.1-1.30.2\"></a>\n## [1.21.1-1.30.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.30.1...1.21.1-1.30.2) - 2025-12-31 14:35:18 +0100\n\n\n### Changed\n* Keep insertion order of network change observers\n  Required for CyclopsMC/IntegratedCrafting#112\n\n<a name=\"1.21.1-1.30.1\"></a>\n## [1.21.1-1.30.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.30.0...1.21.1-1.30.1) - 2025-12-28 09:18:50 +0100\n\n\n### Fixed\n* Add safety checks to disable some operators on infinite lists\n  Concretely, contains, reduce, and uniq are disabled on infinite lists.\n  Closes #1582\n* Fix documentation on NBT Path field selector array indexing (#1580)\n  Closes #1579\n\n<a name=\"1.21.1-1.30.0\"></a>\n## [1.21.1-1.30.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.7...1.21.1-1.30.0) - 2025-12-23 09:44:17 +0100\n\n\n### Added\n* Add squeezer recipes for clearing facades\n\n<a name=\"1.21.1-1.29.7\"></a>\n## [1.21.1-1.29.7](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.6...1.21.1-1.29.7) - 2025-12-19 20:04:37 +0100\n\n\n### Fixed\n* Catch exceptions when deserializing invalid recipes\n\nThis could occur in Integrated Crafting after removing a mod for which a\nrecipe was stored.\n\nCloses CyclopsMC/CommonCapabilities#46\n\n<a name=\"1.21.1-1.29.6\"></a>\n## [1.21.1-1.29.6](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.5...1.21.1-1.29.6) - 2025-11-25 17:01:08 +0100\n\n\n### Fixed\n* Restore network element chunk unloading again, Closes #1571\n\n<a name=\"1.21.1-1.29.5\"></a>\n## [1.21.1-1.29.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.4...1.21.1-1.29.5) - 2025-11-24 16:59:00 +0100\n\n\n### Fixed\n* Temporarily disable chunk unload check again\n\nIt is causing issues with delayers after chunk reloaded.\nOnce a proper fix is available, this will be re-enabled.\n\nRelated to #1571\n\n<a name=\"1.21.1-1.29.4\"></a>\n## [1.21.1-1.29.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.3...1.21.1-1.29.4) - 2025-11-22 11:29:48 +0100\n\n\n### Fixed\n* Restore network element chunk unloading\n  This was temporarily disabled as it broke delayers and proxies on\n  world/chunk reload, which this commit fixes.\n  Closes #1571\n\n<a name=\"1.21.1-1.29.3\"></a>\n## [1.21.1-1.29.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.2...1.21.1-1.29.3) - 2025-11-18 18:47:28 +0100\n\n\n### Fixed\n* Temporarily disable chunk unload check\n  It is causing issues with delayers when worlds/chunks are reloaded.\n  Once a proper fix is available, this will be re-enabled.\n  Related to #1571\n\n<a name=\"1.21.1-1.29.2\"></a>\n## [1.21.1-1.29.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.1...1.21.1-1.29.2) - 2025-11-18 05:33:01 +0100\n\n\n### Added\n* Add translations through Crowdin (#1568)\n\n### Fixed\n* Fix read fluids not always updating in display panel, Closes #1570\n\n<a name=\"1.21.1-1.29.1\"></a>\n## [1.21.1-1.29.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.29.0...1.21.1-1.29.1) - 2025-11-16 14:33:41 +0100\n\n\n### Added\n* Add translations through Crowdin (#1566)\n\n### Fixed\n* Fix ticking network elements preventing chunk unloading, Closes #1567\n\n<a name=\"1.21.1-1.29.0\"></a>\n## [1.21.1-1.29.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.28.1...1.21.1-1.29.0) - 2025-11-11 14:11:35 +0100\n\n\n### Added\n* Add translations through Crowdin (#1554)\n* Add fluid by name operator, Closes #1522\n\n### Changed\n* Ensure consistent order of ingredient types in LP, Closes #1539\n\n### Fixed\n* Fix Grass Block Facades not having biome tints, Closes #1564\n* Fix transparent facades having incorrect alpha value, Closes #1563\n* Fix incorrect item (de)serialization when count is > 99, Closes #1558\n* Fix lists materialization not applying to elements\n  This could cause issues when displaying lists of items from inventory\n  readers over Functional Storage Drawers.\n  Closes #1557\n\n<a name=\"1.21.1-1.28.1\"></a>\n## [1.21.1-1.28.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.28.0...1.21.1-1.28.1) - 2025-10-11 11:39:17 +0200\n\n\n### Changed\n* Improve offset gui error tooltip\n\n### Fixed\n* Fix memory leak when reloading offset variables, Closes #1549\n* Handle overflows in fluid reader total aspects, Closes #1547\n\n<a name=\"1.21.1-1.28.0\"></a>\n## [1.21.1-1.28.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.9...1.21.1-1.28.0) - 2025-10-07 07:47:47 +0200\n\n\n### Added\n* Add Squeezer recipes for Wind Charges\n* Add config option to disable cable collisions(#1538)\n\n### Fixed\n* Properly handle long overflows in channel quantities\n\nInternal changes: (required for Integrated Mekanism)\n* Support regexes in operators_output appendix\n* Abstract parts of recipe LP element\n* Make CommandTest more extensible\n* Make API less dependent on ModBase\n\n<a name=\"1.21.1-1.27.9\"></a>\n## [1.21.1-1.27.9](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.8...1.21.1-1.27.9) - 2025-08-08 21:39:51 +0200\n\n\n### Fixed\n* Fix block capabilities without block entity not being fetched\n  Related to CyclopsMC/IntegratedTunnels#346\n\n<a name=\"1.21.1-1.27.8\"></a>\n## [1.21.1-1.27.8](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.7...1.21.1-1.27.8) - 2025-07-29 17:58:36 +0200\n\n\n### Added\n* Add translations through Crowdin (#1533)\n\n### Fixed\n* Fix display panel being dark when on facade and under block, Closes #1531\n\n<a name=\"1.21.1-1.27.7\"></a>\n## [1.21.1-1.27.7](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.6...1.21.1-1.27.7) - 2025-07-18 20:37:39 +0200\n\n\n### Changed\n* Add PT_BR localization (#1535)\n\n### Fixed\n* Fix crash on load operator variable in LP, Closes #1537\n\n<a name=\"1.21.1-1.27.6\"></a>\n## [1.21.1-1.27.6](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.5...1.21.1-1.27.6) - 2025-07-05 06:55:50 +0200\n\n\n### Added\n* Add translations through Crowdin (#1528)\n\n### Fixed\n* Fix LP crash when loading with empty category card, Closes #1527\n\n<a name=\"1.21.1-1.27.5\"></a>\n## [1.21.1-1.27.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.4...1.21.1-1.27.5) - 2025-06-21 13:28:17 +0200\n\n\n### Changed\n* Return item to player instead of dropping on Labeler exit, Closes #1526\n\n### Fixed\n* Fix fluidstack operator crash on empty items, Closes #1525\n\n<a name=\"1.21.1-1.27.4\"></a>\n## [1.21.1-1.27.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.3...1.21.1-1.27.4) - 2025-06-12 16:38:10 +0200\n\n\n### Added\n* Add translations through Crowdin (#1517)\n\n### Fixed\n* Fix cables not being breakable with wrench, Closes #1523\n\n<a name=\"1.21.1-1.27.3\"></a>\n## [1.21.1-1.27.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.2...1.21.1-1.27.3) - 2025-06-07 18:05:36 +0200\n\n\n### Changed\n* Only switch Wrench mode when aiming at air\n  Closes #1518\n  Closes #1390\n\n### Fixed\n* Fix overlapping tooltips in LP write slot, Closes #1519\n* Fix typos in language file\n\n<a name=\"1.21.1-1.27.2\"></a>\n## [1.21.1-1.27.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.1...1.21.1-1.27.2) - 2025-05-31 21:32:50 +0200\n\n\n### Fixed\n* Reorder events to fix addon mod icons not loading\n\n<a name=\"1.21.1-1.27.1\"></a>\n## [1.21.1-1.27.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.27.0...1.21.1-1.27.1) - 2025-05-31 20:29:24 +0200\n\n\n### Fixed\n* Fix missing variable by id aspect texture\n\n<a name=\"1.21.1-1.27.0\"></a>\n## [1.21.1-1.27.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.26.2...1.21.1-1.27.0) - 2025-05-31 19:58:49 +0200\n\n\n### Added\n* Allow editing existing variables in Logic Programmer, Closes #357\n* Add Variable Value By ID operator aspect to network reader, Closes #1346\n* Add button to reset selected LP element\n\n### Changed\n* Don't drop item in world when closing LP\n* Improve LP element arrow\n\n### Fixed\n* Fix operator text field in LP sometimes being red\n\n<a name=\"1.21.1-1.26.2\"></a>\n## [1.21.1-1.26.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.26.1...1.21.1-1.26.2) - 2025-05-25 07:01:45 +0200\n\n\n### Added\n* Add translations through Crowdin\n\n### Fixed\n* Fix cursor centering on gui switching, Closes CyclopsMC/IntegratedDynamics#1514\n\n<a name=\"1.21.1-1.26.1\"></a>\n## [1.21.1-1.26.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.26.0...1.21.1-1.26.1) - 2025-05-20 17:32:38 +0200\n\n\n### Added\n* Add translations through Crowdin\n\n### Fixed\n* Fix search hotkey also typing in hotkey in search box\n  Closes CyclopsMC/IntegratedTerminals#168\n\n<a name=\"1.21.1-1.26.0\"></a>\n## [1.21.1-1.26.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.12...1.21.1-1.26.0) - 2025-05-03 16:32:19 +0200\n\n\n### Added\n* Add list set and multiset equality operators, Closes #1347\n* Add Fluid with tag operator, Closes #797\n* Add block and fluid tag operators, Closes #1394\n* Add Sqrt and Pow operators, Closes #1372\n* Add lectern and bookshelf support for infobook, Closes #1496\n\n### Fixed\n* Fix non-fluid items being insertable into LP, Closes #1507\n\n<a name=\"1.21.1-1.25.12\"></a>\n## [1.21.1-1.25.12](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.11...1.21.1-1.25.12) - 2025-04-20 15:32:52 +0200\n\n\n### Added\n* Add skull-based note support to audio reader and writer\n\n### Fixed\n* Fix wrench not removing cables after using off-hand item, Closes #1504\n* Fix proxies placed by non-players not having an id\n\n<a name=\"1.21.1-1.25.11\"></a>\n## [1.21.1-1.25.11](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.10...1.21.1-1.25.11) - 2025-04-04 17:12:35 +0200\n\n\n### Added\n* Add compostables, Closes #1498\n* Add stripped logs and woods tags, Closes #1500\n\n<a name=\"1.21.1-1.25.10\"></a>\n## [1.21.1-1.25.10](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.9...1.21.1-1.25.10) - 2025-03-22 15:14:29 +0100\n\n\n### Added\n* Add ja_jp translations through Crowdin (#1492)\n\n### Fixed\n* Fix NBT from int list not working for any lists\n  Closes CyclopsMC/IntegratedScripting#37\n* Fix typos in manual\n* Fixed number typo\n* Fixed variable description\n* Fixed typo in reader introduction\n\n<a name=\"1.21.1-1.25.9\"></a>\n## [1.21.1-1.25.9](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.8...1.21.1-1.25.9) - 2025-03-12 14:41:33 +0100\n\n\nAdded:\n* Add translations through Crowdin (#1475)\n  Co-authored-by: Crowdin Bot <support+bot@crowdin.com>\n\n### Fixed\n* Fix crash for items in variables with stacksize > 99\n\n<a name=\"1.21.1-1.25.8\"></a>\n## [1.21.1-1.25.8](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.7...1.21.1-1.25.8) - 2025-03-10 06:58:36 +0100\n\n\n### Added\n* Update ru_ru.json (#1483)\n\n### Changed\n* Avoid repeated log spam for forcefully unloaded parts, Closes #1481\n\n<a name=\"1.21.1-1.25.7\"></a>\n## [1.21.1-1.25.7](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.6...1.21.1-1.25.7) - 2025-03-01 07:09:23 +0100\n\n\n### Changed\n* Improve performance of block changes to very large networks\n  Pathfinder is ~70x faster, also improved tick speed with lots of devices (17 ms to 3 ms in test scene)\n* Remove reference to \"crystallised menril resin\"\n\n### Fixed\n* Fix duplication of offset enchancements when breaking cable, Closes #1480\n* Fix passive interaction changes not immediately applying on change\n  It used to only take effect after re-inserting a variable card.\n  Closes CyclopsMC/IntegratedDynamics#1470\n* Fix tooltip typo for static light panel\n* Fix mention of \"degrees Kelvin\"\n* Fix \"raining\" grammar sentence in aspect\n\n<a name=\"1.21.1-1.25.6\"></a>\n## [1.21.1-1.25.6](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.5...1.21.1-1.25.6) - 2025-02-22 17:06:18 +0100\n\n\n### Fixed\n* Fix GUIs remaining open after external breakage, Closes #1472\n* Fix rare tooltip crash for item-based variable cards, Closes #1477\n\n<a name=\"1.21.1-1.25.5\"></a>\n## [1.21.1-1.25.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.4...1.21.1-1.25.5) - 2025-02-15 10:20:09 +0100\n\n\n### Fixed\n* Fix broken advancement icons\n* Fix broken looking at advancement trigger, #1471\n* Fix broken clear facade recipe\n\n<a name=\"1.21.1-1.25.4\"></a>\n## [1.21.1-1.25.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.3...1.21.1-1.25.4) - 2025-02-08 16:00:00 +0100\n\n\n### Fixed\n* Fix LP recipe slot properties not displaying tag tooltip items\n* Fix reusable tag-based recipes always having strict NBT\n  This would cause issues for autocrafting jobs where tag-based reusable\n  items that take damage would not be reused from the moment they take\n  damage.\n  Closes CyclopsMC/IntegratedCrafting#129\n* Fix cable model updating neighbours from wrong thread, Closes #1465\n\n<a name=\"1.21.1-1.25.3\"></a>\n## [1.21.1-1.25.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.2...1.21.1-1.25.3) - 2025-02-03 17:09:55 +0100\n\n\n### Changed\n* Add translations through Crowdin (#1455)\n\n### Fixed\n* Fix REI recipe transfer to LP not working for fluids\n* Fix LP recipe transfer including chance-based outputs\n  Non-1000utputs could cause issues for autocrafting.\n  This is fixed for both JEI and REI.\n  Closes CyclopsMC/IntegratedCrafting#127\n* Fix tag-based JEI recipe transfer ignoring stack sizes\n  Closes CyclopsMC/IntegratedCrafting#128\n* Fix placing part before cable not properly connecting cables\n  Closes CyclopsMC/IntegratedTunnels#321\n\n<a name=\"1.21.1-1.25.2\"></a>\n## [1.21.1-1.25.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.1...1.21.1-1.25.2) - 2025-01-13 16:11:55 +0100\n\n\n### Fixed\n* Fix conflicting item/fluid data value operator names, Closes #1462\n\n<a name=\"1.21.1-1.25.1\"></a>\n## [1.21.1-1.25.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.25.0...1.21.1-1.25.1) - 2025-01-09 16:10:11 +0100\n\n\n### Fixed\n* Fix crash when passing any list to Ingr.with_items, Closes #1457\n* Fix lighting issues when displaying items or ingredients, Closes #1458\n\n<a name=\"1.21.1-1.25.0\"></a>\n## [1.21.1-1.25.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.24.3...1.21.1-1.25.0) - 2025-01-08 17:42:56 +0100\n\n\n### Added\n* Add item tooltip operators\n\n### Fixed\n* Fix incorrect player entity aspect description\n\n<a name=\"1.21.1-1.24.3\"></a>\n## [1.21.1-1.24.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.24.2...1.21.1-1.24.3) - 2024-12-28 14:00:33 +0100\n\n\n### Added\n* Add lossy recipe to clear a facade in a crafting grid, Closes #1424\n\n### Changed\n* Reduce number of network events during init to improve performance, #1439\n\n### Fixed\n* Fix client-server desync when applying part offsets, Closes #1448\n* Fix offset items only being applicable to 28 instead of 32, #1448\n\n<a name=\"1.21.1-1.24.2\"></a>\n## [1.21.1-1.24.2](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.24.1...1.21.1-1.24.2) - 2024-12-24 09:53:40 +0100\n\n\n### Changed\n* Add cs_cz translations\n\n### Fixed\n* Reduce number of network events during init to improve performance, #1439\n  Modifying very large networks is a lot faster now.\n\n<a name=\"1.21.1-1.24.1\"></a>\n## [1.21.1-1.24.1](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.24.0...1.21.1-1.24.1) - 2024-12-17 11:12:43 +0100\n\n\n### Added\n* Add error operator\n* Add nl_nl translations\n\n### Changed\n* Reduce network inits by half when placing cables, Closes #1439\n\n### Fixed\n* Fix Jade integration not working, Closes #1438\n\n<a name=\"1.21.1-1.24.0\"></a>\n## [1.21.1-1.24.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.19...1.21.1-1.24.0) - 2024-12-06 16:10:51 +0100\n\n\n### Added\n* Add dedicated REI support, Closes CyclopsMC/IntegratedDynamics#1348\n* Restore Jade/Waila integration, Closes CyclopsMC/IntegratedDynamics#1413\n\n### Changed\n* Remove OnlyIn from appendHoverText and BlockCable\n\n### Fixed\n* Fix network elements not being removed when broken as non-player\n  This fixes exceptions and console spam when removing cables with commands.\n  Related to #443\n* Fix server sometimes hanging after shutdown\n  Related to #1415\n* Fix cable placement with commands initializing networks\n  This was broken since CyclopsMC/IntegratedTunnels#243\n\n<a name=\"1.21.1-1.23.19\"></a>\n## [1.21.1-1.23.19](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.18...1.21.1-1.23.19) - 2024-11-22 10:08:55 +0100\n\n\n### Fixed\n* Fix capability crash when getting null direction from materializer, Closes #1423\n\n<a name=\"1.21.1-1.23.18\"></a>\n## [1.21.1-1.23.18](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.17...1.21.1-1.23.18) - 2024-11-22 07:22:06 +0100\n\n\n### Fixed\n* Fix silent error when deserializing empty item values, Closes #1422\n\n<a name=\"1.21.1-1.23.17\"></a>\n## [1.21.1-1.23.17](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.16...1.21.1-1.23.17) - 2024-11-19 15:17:52 +0100\n\n\n### Changed\n* Drop Part Offsets into their original form, Closes #1418\n\n### Fixed\n* Fix creative batteries not providing energy, Closes #1421\n\n<a name=\"1.21.1-1.23.16\"></a>\n## [1.21.1-1.23.16](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.15...1.21.1-1.23.16) - 2024-11-10 14:14:54 +0100\n\n\n### Fixed\n* Fix variables dependent on offset aspects not always updating, Closes #1416\n\n<a name=\"1.21.1-1.23.15\"></a>\n## [1.21.1-1.23.15](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.14...1.21.1-1.23.15) - 2024-11-02 16:04:05 +0100\n\n\n### Changed\n* Optimize ingredient positions index lookups\n  Closes CyclopsMC/IntegratedTunnels#307\n\n### Fixed\n* Fix deadlock when getting light level of cable\n  This fixes an incompatibility with the Moonrise mod.\n  Closes #1415\n* Fix Deepslate Dark Ore not being squeezable, Closes #1414\n\n<a name=\"1.21.1-1.23.14\"></a>\n## [1.21.1-1.23.14](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.13...1.21.1-1.23.14) - 2024-10-30 18:35:50 +0100\n\n\n### Fixed\n* Fix crash with complex Integrated Scripting functions in writers\n\nFunctions returning an any type could be inserted into writers such as\nIntegrated Tunnels exporters, and type checking would incorrectly pass.\nThis could result in crashes where an incorrect value cast would occur.\nThis commit makes it so that ANY types will have an additional type\ncheck based on the actual determined value.\n\nCloses CyclopsMC/IntegratedScripting#20\n\n<a name=\"1.21.1-1.23.13\"></a>\n## [1.21.1-1.23.13](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.12...1.21.1-1.23.13) - 2024-10-30 17:03:41 +0100\n\n\n### Fixed\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 8f53b7eb53302e5028d1b4414caf288d26eb73ee.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n<a name=\"1.21.1-1.23.12\"></a>\n## [1.21.1-1.23.12](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.11...1.21.1-1.23.12) - 2024-10-29 18:52:27 +0100\n\n\n### Fixed\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 61b7372bda5cde123a2b320ac92c1c24d2dfb9b1.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n<a name=\"1.21.1-1.23.11\"></a>\n## [1.21.1-1.23.11](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.10...1.21.1-1.23.11) - 2024-10-28 17:26:18 +0100\n\n\n### Changed\n* Optimize ingredient positions index\n  This improves performance with Integrated Tunnels and Terminals.\n\n<a name=\"1.21.1-1.23.10\"></a>\n## [1.21.1-1.23.10](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.9...1.21.1-1.23.10) - 2024-10-24 16:50:13 +0200\n\n\n### Fixed\n* Restore placement of levers on parts, Closes #1408\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n* Fix unable to insert into part offset slots, Closes #1409\n\n<a name=\"1.21.1-1.23.9\"></a>\n## [1.21.1-1.23.9](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.8...1.21.1-1.23.9) - 2024-10-14 15:19:24 +0200\n\n\n### Fixed\n* Fix incorrect type checking in complex reduce operation, Closes #1387\n\n<a name=\"1.21.1-1.23.8\"></a>\n## [1.21.1-1.23.8](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.7...1.21.1-1.23.8) - 2024-10-06 14:03:24 +0200\n\n\n### Fixed\n* Fix crash when connecting cables with redstone with Sodium\n\n<a name=\"1.21.1-1.23.7\"></a>\n## [1.21.1-1.23.7](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.6...1.21.1-1.23.7) - 2024-10-03 19:11:53 +0200\n\n\n### Changed\n* Improve performance by posting AttachCapabilitiesEventPart to other bus, Closes #1400\n\n### Fixed\n* Fix JEI ghosts items not working for lists in the LP\n  Closes CyclopsMC/IntegratedDynamics#1398\n* Fix wrong sided part being shown in The One Probe, Closes #1401\n* Fix auto-supply on batteries not working, Closes #1399\n* Fix round-robin misbehaving with filtered interfaces, Closes CyclopsMC/IntegratedTunnels#302\n\n<a name=\"1.21.1-1.23.6\"></a>\n## [1.21.1-1.23.6](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.5...1.21.1-1.23.6) - 2024-09-23 17:26:41 +0200\n\n\n### Fixed\n* Fix errors in conditional squeezer recipes, Closes CyclopsMC/CyclopsCore#191\n* Fix crash when placing creative energy battery, Closes #1397\n\n<a name=\"1.21.1-1.23.5\"></a>\n## [1.21.1-1.23.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.4...1.21.1-1.23.5) - 2024-09-17 20:28:05 +0200\n\n\n### Changed\n* Combine cable voxel shape components in getShape rather than getCollisionShape\n\n### Fixed\n* Fix broken offset enchancement recipe, Closes #1367\n* Fix crash when placing Proxy or Materializer in SMP, Closes #1392\n\n<a name=\"1.21.1-1.23.4\"></a>\n## [1.21.1-1.23.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.3...1.21.1-1.23.4) - 2024-08-24 07:17:19 +0200\n\n\n### Fixed\n* Delay Terrablender registration\n  This fixes rare crashes when Terrablender was not yet fully initialized.\n  CyclopsMC/IntegratedDynamics#1385\n\n<a name=\"1.21.1-1.23.3\"></a>\n## [1.21.1-1.23.3](https://github.com/CyclopsMC/IntegratedDynamics/compare/1.21.1-1.23.2...1.21.1-1.23.3) - 2024-08-22 19:01:40 +0200\n\n\n### Fixed\n* Fix Redstone Writers not always updating signals\n  Closes #1377\n  Closes #1382\n* Fix crash when getting facade colors for specific blocks, Closes #1380\n* Fix op_by_name crashing if ResourceLocation is invalid, Closes #1381\n* Fix regex scan producing illegal lists for non-zero groups, Closes #1378\n* Refer to NeoForge's updateJSONURL instead of Forge's\n* Fixed Feature Order Cycle (FOC)\n\n<a name=\"1.21.1-1.23.2\"></a>\n## [1.21.1-1.23.2] - 2024-08-09 21:06:15 +0200\n\n\n### Changed\n* Update to updated CommonCapabilities API\n  Required for CyclopsMC/IntegratedDynamics#1375\n"
  },
  {
    "path": "CHANGELOG-26.1.1.md",
    "content": "# Changelog for Minecraft 26.1.1\nAll notable changes to this project will be documented in this file.\n\n<a name=\"26.1.1-1.32.11\"></a>\n## [26.1.1-1.32.11](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.10...26.1.1-1.32.11) - 2026-04-20 18:51:08\n\n\n### Fixed\n* Remove old Tesla references from language files\n* Fix battery capacity not persisting after world rejoin/restart (#1667)\n\n<a name=\"26.1.1-1.32.10\"></a>\n## [26.1.1-1.32.10](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.9...26.1.1-1.32.10) - 2026-04-19 14:49:50 +0200\n\n\n### Added\n* Add translations through Crowdin (#1663)\n\n### Fixed\n* Fix Menril Saplings not growing with bonemeal, Closes #1664\n\n<a name=\"26.1.1-1.32.9\"></a>\n## [26.1.1-1.32.9](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.8...26.1.1-1.32.9) - 2026-04-19 14:03:00 +0200\n\n\n### Fixed\n* Fix NPE in EnergyStorageItemBlockEnergyContainer when itemAccess is null (#1665)\n\n<a name=\"26.1.1-1.32.8\"></a>\n## [26.1.1-1.32.8](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.7...26.1.1-1.32.8) - 2026-04-18 15:35:10 +0200\n\n\n### Changed\n* Add BlockCable#getAppearance for connected textures on facades\n  This adds basic support to facades on cables for mods that add connected texture support.\n\n<a name=\"26.1.1-1.32.7\"></a>\n## [26.1.1-1.32.7](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.6...26.1.1-1.32.7) - 2026-04-17 18:30:20 +0200\n\n\n### Fixed\n* Fix worldgen: prevent menril trees from generating on top of other trees (#1662), Closes #1660\n\n<a name=\"26.1.1-1.32.6\"></a>\n## [26.1.1-1.32.6](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.5...26.1.1-1.32.6) - 2026-04-17 16:49:22 +0200\n\n\n### Changed\n* Add leaf particle colors for Menril Leaves\n\n### Fixed\n* Fix crash when rendering empty battery item stack (#1661)\n\n<a name=\"26.1.1-1.32.5\"></a>\n## [26.1.1-1.32.5](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.4...26.1.1-1.32.5) - 2026-04-13 16:48:01 +0200\n\n\n### Added\n* Add c:clumps tag to Crystalized Menril Chunk (#1659), Closes #1656\n* Add translations through Crowdin (#1649)\n\n### Fixed\n* Fix Menril Resin and Liquid Chorus fluids being non-interactable in world (#1658), Closes #1657\n* Fix drying basin stalling when both item and fluid inputs are present (#1655)\n* Fix drying basin not consuming items when right-clicked with a stack (#1654), Closes #1652\n* Fix incorrect item model for Menril Sapling\n\n<a name=\"26.1.1-1.32.4\"></a>\n## [26.1.1-1.32.4](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.1-1.32.3...26.1.1-1.32.4) - 2026-04-11 20:05:09 +0200\n\n\n### Changed\n* Restore JEI and Terrablender mod compats\n\n### Fixed\n* Fix constant list Variable Card deserialization failure after world reload (#1639), Closes #1639\n* Give Delayer its own energy consumption config separate from Proxy (#1641), Closes #1640\n\n<a name=\"26.1.1-1.32.3\"></a>\n## [26.1.1-1.32.3] - 2026-04-10 14:38:29 +0200\n\n\nInitial 26.1.1 release\n"
  },
  {
    "path": "CHANGELOG-26.1.2.md",
    "content": "# Changelog for Minecraft 26.1.2\nAll notable changes to this project will be documented in this file.\n\n<a name=\"26.1.2-1.33.0\"></a>\n## [26.1.2-1.33.0](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.2-1.32.13...26.1.2-1.33.0) - 2026-05-04 16:37:42\n\n\n### Added\n* Add translations through Crowdin (#1671)\n* Update Refined Storage compat to v2, Closes CyclopsMC/IntegratedDynamics#1544\n\n<a name=\"26.1.2-1.32.13\"></a>\n## [26.1.2-1.32.13](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.2-1.32.12...26.1.2-1.32.13) - 2026-05-02 20:40:51 +0200\n\n\n### Fixed\n* Fix Crafting Interface crash on Mechanical Drying Basin, Closes CyclopsMC/IntegratedCrafting#197\n\n<a name=\"26.1.2-1.32.12\"></a>\n## [26.1.2-1.32.12](https://github.com/CyclopsMC/IntegratedDynamics/compare/26.1.2-1.32.11...26.1.2-1.32.12) - 2026-04-28 19:44:46 +0200\n\n\n### Fixed\n* Fix incorrect position of channel disabled tooltip in Integrated Crafting\n\n<a name=\"26.1.2-1.32.11\"></a>\n## [26.1.2-1.32.11] - 2026-04-23 20:21:26 +0200\n\n\nInitial 26.1.2 release\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Cyclops\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "PERFORMANCE_BENCHMARKING.md",
    "content": "# Performance Benchmarking Setup\n\nThis document describes the performance benchmarking infrastructure for Integrated Dynamics, which measures the continuous performance of network operations.\nPerformance results are tracked in https://github.com/CyclopsMC/cyclops-performance-results\n\n## Overview\n\nThe performance benchmarking system consists of three main components:\n\n1. **GitHub Workflow** (`.github/workflows/performance.yml`)\n   - Executes on the same triggers as CI (push and pull_request)\n   - Runs game tests to measure network performance\n   - Uses `benchmark-action/github-action-benchmark` to track performance evolution\n\n2. **Game Tests** (`src/integrationtest/java/org/cyclops/integrateddynamics/gametest/GameTestsPerformance.java`)\n   - Generates networks with different presets for benchmarking\n   - Measures performance metrics for each preset\n   - Writes results to `build/logs/benchmark_results.txt`\n\n3. **Network Generation Command** (`src/main/java/org/cyclops/integrateddynamics/command/CommandGenerateNetwork.java`)\n   - Provides `/integrateddynamics generatenetwork` command for manual testing\n   - Supports different network presets: `emptynetwork`, `idlenetwork`, `clear`\n   - Can be used in both single-player and multiplayer environments\n\n## Network Presets\n\n### Empty Network\n- **Preset**: `empty`\n- **Description**: A cube of logic cables with no parts attached\n- **Benchmark**: Measures baseline performance with cable networks only\n- **Sizes tested**: 25×25×25\n\n### Idle Network\n- **Preset**: `idle`\n- **Description**: A cube of logic cables with random parts on all outer surfaces\n- **Benchmark**: Measures performance with active network elements (parts)\n- **Sizes tested**: 25×25×25\n\n### Redstone IO Clock Network\n- **Preset**: `redstoneioclock`\n- **Description**: A cube of logic cables with redstone readers on the east side connected to redstone writers on the west side, creating a clock signal\n- **Benchmark**: Measures performance with active parts\n- **Sizes tested**: 25×25×25\n\n### Redstone IO Clock with Variables Network\n- **Preset**: `redstoneioclockvariables`\n- **Description**: A cube of logic cables with redstone readers on the east side connected to redstone writers on the west side, creating a clock signal with variables in between.\n- **Benchmark**: Measures performance with active parts and operator variables\n- **Sizes tested**: 25×25×25\n\n## Performance Metrics\n\nThe benchmarking system measures two key metrics:\n\n- **Average Network Tick Time (ms)**: The average time the Integrated Dynamics network subsystem takes to process one game tick. This measures the network-specific performance impact.\n- **Average Server Tick Time (ms)**: The average time the entire Minecraft server takes per game tick. This measures the overall server performance impact, including both Integrated Dynamics and all other server operations.\n- **Network Size**: The dimensions of the generated network cube\n\nThese metrics are tracked separately in the benchmark results to distinguish between network-specific performance and overall server performance impact.\n\n## Game Test Execution\n\nThe game tests are automatically executed as part of the GitHub workflow:\n\n```bash\nPERFORMANCE_BENCHMARK_ENABLED=true ./gradlew runGameTestServer\n```\n\nThis command:\n1. Starts a game test server\n2. Runs `GameTestsPerformance` game tests\n3. Generates networks of different sizes with different presets\n4. Measures performance metrics\n5. Writes results to `run/logs/benchmark_results.txt`\n\nSimilarly, if you want to run the fuzzing tests, you can use:\n```bash\nFUZZING_ITERATIONS=10 ./gradlew runGameTestServer\n```\n\n## Result Format\n\nResults are written in the following format:\n```\npreset=empty size=25 avgNetworkTickTime=6.25 avgServerTickTime=3.50\npreset=idle size=25 avgNetworkTickTime=7.50 avgServerTickTime=4.20\n```\n\nResults are then converted to JSON format for the benchmark action. Each preset generates two metrics - one for network tick time and one for server tick time:\n```json\n[\n  {\n    \"name\": \"empty_size_25_network_tick_time\",\n    \"unit\": \"ms\",\n    \"value\": 6.25\n  },\n  {\n    \"name\": \"empty_size_25_server_tick_time\",\n    \"unit\": \"ms\",\n    \"value\": 3.50\n  }\n]\n```\n\n## Benchmark Tracking\n\nThe `benchmark-action/github-action-benchmark` GitHub action automatically:\n- Stores benchmark results in GitHub Pages branch (`gh-pages`)\n- Generates historical performance charts\n- Alerts when performance degrades beyond 150% of baseline\n- Creates comments on PRs when alerts are triggered\n\n## Manual Testing\n\nTo manually test network performance in a Minecraft world:\n\n1. **Generate an empty network**:\n   ```\n   /integrateddynamics generatenetwork empty 50\n   ```\n\n2. **Generate an idle network**:\n   ```\n   /integrateddynamics generatenetwork idle 50\n   ```\n\n3. **Measure network performance**:\n   ```\n   /integrateddynamics networkdiagnostics measure 10\n   ```\n\n4. **Clear generated networks**:\n   ```\n   /integrateddynamics generatenetwork clear 100\n   ```\n\n## Integration with CI/CD\n\nThe performance workflow runs on:\n- Every push to any branch\n- Every pull request\n\nPerformance degradation is tracked across commits and branches, helping to identify performance regressions early in the development cycle.\n\n## Adding New Benchmarks\n\nTo add new network presets or benchmarks:\n\n1. Add a new preset enum value in `CommandGenerateNetwork.NetworkPreset`\n2. Add corresponding generation method in `CommandGenerateNetworkExecutor`\n3. Add a new `@GameTest` method in `GameTestsPerformance`\n4. The workflow will automatically execute and track the new benchmark\n\n"
  },
  {
    "path": "README.md",
    "content": "## Integrated Dynamics\n\n[![Build Status](https://github.com/CyclopsMC/IntegratedDynamics/workflows/CI/badge.svg)](https://github.com/CyclopsMC/IntegratedDynamics/actions?query=workflow%3ACI)\n[![Coverage Status](https://coveralls.io/repos/github/CyclopsMC/IntegratedDynamics/badge.svg)](https://coveralls.io/github/CyclopsMC/IntegratedDynamics)\n[![Crowdin](https://badges.crowdin.net/cyclopsmc-integrateddynamic/localized.svg)](https://crowdin.com/project/cyclopsmc-integrateddynamic)\n[![Download](https://img.shields.io/static/v1?label=Maven&message=GitHub%20Packages&color=blue)](https://github.com/CyclopsMC/packages/packages/769739)\n[![CurseForge](http://cf.way2muchnoise.eu/full_236307_downloads.svg)](https://www.curseforge.com/minecraft/mc-mods/integrated-dynamics)\n[![Discord](https://img.shields.io/discord/386052815128100865.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/9yDxubB)\n\nAll stable releases (including deobfuscated builds) can be found on [CurseForge](https://www.curseforge.com/minecraft/mc-mods/integrated-dynamics/files).\n\n[Development builds](https://github.com/CyclopsMC/packages/packages/) are hosted as GitHub packages.\n\n### Contributing\n* Before submitting a pull request containing a new feature, please discuss this first with one of the lead developers.\n* When fixing an accepted bug, make sure to declare this in the issue so that no duplicate fixes exist.\n* All code must comply to our coding conventions, be clean and must be well documented.\n\n### Issues\n* All bug reports and other issues are appreciated. If the issue is a crash, please include the FULL Forge log.\n* Before submission, first check for duplicates, including already closed issues since those can then be re-opened.\n\n### Branching Strategy\n\nFor every major Minecraft version, a `master-{mc_version} branch exists.\n\n### Building and setting up a development environment\n\nTo get started, you will need a [Maven token](https://github.com/settings/tokens/new?scopes=read:packages&description=GPR%20for%20Gradle) so that gradle can fetch dependencies.\nOnce you have this, use it by editing `~/.gradle/gradle.properties` and adding the following lines;\n```\ngpr.user=<YOUR GITHUB USERNAME>\ngpr.key=<YOUR TOKEN>\n```\nAlternatively, you can use the environment variables `MAVEN_USERNAME` (your github username), and `MAVEN_KEY` (your token).\n\nRun `./gradlew test` to run the test suite, and `./gradlew build` to build the mod. The resulting jar file will be in `build/libs`.\n\n### License\nAll code and images are licensed under the [MIT License](https://github.com/CyclopsMC/IntegratedDynamics/blob/master-1.8/LICENSE.txt)\nThis mod packages [re2j](https://github.com/google/re2j/) in order to provide linear time regex matching.\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id 'net.neoforged.moddev' version '2.0.141'\n    id 'net.darkhax.curseforgegradle' version '1.1.28'\n    id 'com.diffplug.spotless' version '6.25.0'\n    id 'com.gradleup.shadow' version '8.3.8'\n\tid 'com.modrinth.minotaur' version '2.+'\n}\n\napply plugin: 'idea'\napply plugin: 'maven-publish'\napply plugin: 'jacoco'\n\nimport net.darkhax.curseforgegradle.Constants\nimport net.darkhax.curseforgegradle.TaskPublishCurseForge\n\n// Determine build number\nString build_number = \"DEV\" // this will be referenced as simply build_number from now on.\nif (System.getenv().BUILD_NUMBER)\n    build_number = System.getenv().BUILD_NUMBER\nif (System.getenv().GITHUB_RUN_ID)\n    build_number = System.getenv().GITHUB_RUN_NUMBER\nif (System.getenv().RELEASE)\n    build_number = \"RELEASE\"\nlogger.lifecycle \"BUILDING VERSION: \" + build_number\n\n// Include build number in version\nif (build_number.equals(\"RELEASE\"))\n    version = \"${project.mod_version}\"\nelse\n    version = \"${project.mod_version}-${build_number}\"\n\nbase {\n    archivesName = \"${mod_id}-${minecraft_version}-neoforge\"\n}\n\njava {\n    toolchain.languageVersion = JavaLanguageVersion.of(java_version)\n}\ncompileJava.options.compilerArgs << \"-Xmaxerrs\" << \"9999\"\n\n// Load secrets\ndef getSecrets() {\n    Properties props = new Properties()\n    if (file('./secrets.properties').exists()) {\n        props.load(new FileInputStream(file('./secrets.properties')))\n    }\n    return props\n}\ndef getCurseforgeKey() {\n    if(secrets.curseforge_key) {\n        return secrets.curseforge_key\n    } else if(System.getenv().GITHUB_ACTIONS && System.getenv().CURSEFORGE_KEY_SECRET) {\n        return System.getenv().CURSEFORGE_KEY_SECRET\n    } else {\n        return \"DUMMY\"\n    }\n}\ndef getModrinthKey() {\n    if(secrets.modrinth_key) {\n        return secrets.modrinth_key\n    } else if(System.getenv().GITHUB_ACTIONS && System.getenv().MODRINTH_KEY_SECRET) {\n        return System.getenv().MODRINTH_KEY_SECRET\n    } else {\n        return \"DUMMY\"\n    }\n}\ndef getChangelog() {\n    if (System.getenv().RELEASE) {\n        return file(\"./CHANGELOG-${project.minecraft_version}.md\").getText();\n    } else {\n        // Check if any release tag exists for this Minecraft version\n        def proc = ['git', 'tag', '--list', \"${project.minecraft_version}-*\"].execute([], project.rootProject.projectDir)\n        def tags = proc.text.trim()\n        if (tags) {\n            return \"Changes since last release: ${project.github_url}/compare/${project.minecraft_version}-${project.mod_version}...${System.getenv().GITHUB_SHA}\"\n        } else {\n            // No release has been made yet for this Minecraft version, link to all commits on the branch\n            def branch = System.getenv().GITHUB_REF_NAME ?: 'HEAD'\n            return \"Changes since last release: ${project.github_url}/commits/${branch}\"\n        }\n    }\n}\n//ext {\n    Properties secrets = getSecrets();\n    secrets.curseforgeKey = getCurseforgeKey();\n    secrets.modrinthKey = getModrinthKey();\n    secrets.changelog = getChangelog();\n    secrets.build_number = build_number;\n//}\n\nsourceSets {\n    main\n    test\n}\nsourceSets.main.resources { srcDir 'src/generated/resources' }\n\nconfigurations {\n    shadow.setTransitive(false);\n\n    modLib\n    implementation.extendsFrom modLib\n}\n\ntest {\n    testLogging {\n        exceptionFormat = 'full'\n    }\n}\n\nrepositories {\n    mavenCentral()\n\tmavenLocal()\n    maven {\n        name \"Cyclops Repo\"\n        url \"https://maven.pkg.github.com/CyclopsMC/packages\"\n        credentials {\n            username = project.findProperty(\"gpr.user\") ?: System.getenv(\"MAVEN_USERNAME\") ?: System.getenv(\"GITHUB_USER\")\n            password = project.findProperty(\"gpr.key\") ?: System.getenv(\"MAVEN_KEY\") ?: System.getenv(\"GITHUB_TOKEN\")\n        }\n    }\n}\n\ndependencies {\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'\n    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.1'\n    testImplementation 'org.junit.platform:junit-platform-suite-api:1.14.1'\n    testImplementation 'org.hamcrest:hamcrest:2.1'\n    testImplementation \"org.mockito:mockito-core:2.+\"\n    testRuntimeOnly 'org.junit.platform:junit-platform-suite-engine'\n    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n\n    // Add something like 'cyclopscore_version_local=0.1.0-DEV' to your secrets.properties if you want to use a custom local CyclopsCore version.\n    if(secrets.cyclopscore_version_local) {\n        implementation \"org.cyclops.cyclopscore:cyclopscore-${project.minecraft_version}-neoforge:${secrets.cyclopscore_version_local}\"\n    } else {\n        implementation \"org.cyclops.cyclopscore:cyclopscore-${project.minecraft_version}-neoforge:${project.cyclopscore_version}\"\n    }\n\t\n    // Add something like 'integrateddynamicscompat_version_local=0.1.0-DEV' to your secrets.properties if you want to use a custom local Integrated Dynamics Compat version.\n    if(secrets.integrateddynamicscompat_version_local) {\n        shadow(\"org.cyclops.integrateddynamicscompat:integrateddynamicscompat-${project.minecraft_version}-neoforge:${secrets.integrateddynamicscompat_version_local}\") {\n            transitive = false\n        }\n    } else {\n        shadow(\"org.cyclops.integrateddynamicscompat:integrateddynamicscompat-${project.integrateddynamicscompat_version}\") {\n            transitive = false\n        }\n    }\n\n    if(secrets.commoncapabilities_version_local) {\n        implementation \"org.cyclops.commoncapabilities:commoncapabilities-${project.minecraft_version}-neoforge:${secrets.commoncapabilities_version_local}\"\n    } else {\n        implementation(\"org.cyclops.commoncapabilities:commoncapabilities-${project.minecraft_version}-neoforge:${project.commoncapabilities_version}\") { // https://dl.bintray.com/cyclopsmc/dev/org/cyclops/commoncapabilities/CommonCapabilities/\n            transitive = false\n        }\n    }\n\t\n    modLib \"com.google.re2j:re2j:1.1\"\n    shadow \"com.google.re2j:re2j:1.1\"\n\n    modLib (\"io.netty:netty-codec-http:4.1.77.Final\") {\n        transitive = false\n    }\n    shadow \"io.netty:netty-codec-http:4.1.77.Final\"\n}\n\ntest {\n    useJUnitPlatform()\n}\n\nneoForge {\n    version = \"${neoforge_version}\"\n\n    // Automatically enable neoforge AccessTransformers if the file exists\n    def at = file('src/main/resources/META-INF/accesstransformer.cfg')\n    if (at.exists()) {\n        accessTransformers = [ at.absolutePath ]\n    }\n\n    validateAccessTransformers = true\n\n    runs {\n        client {\n            client()\n        }\n        server {\n            server()\n        }\n        gameTestServer {\n            type = \"gameTestServer\"\n        }\n        configureEach {\n            systemProperty 'forge.logging.markers', 'REGISTRIES'\n            logLevel = org.slf4j.event.Level.DEBUG\n            systemProperty 'neoforge.enabledGameTestNamespaces', \"cyclopscore,${mod_id}\"\n        }\n    }\n\n    mods {\n        \"${mod_id}\" {\n            sourceSet(sourceSets.main)\n        }\n    }\n\n    unitTest {\n        enable()\n        testedMod = mods.integrateddynamics\n    }\n}\n\njar {\n    archiveClassifier.set('slim')\n    manifest {\n        attributes([\n                \"FMLAT\": \"accesstransformer.cfg\",\n                \"Specification-Title\": \"${project.name}\",\n                \"Specification-Vendor\": \"rubensworks\",\n                \"Specification-Version\": \"${project.mod_version}\",\n                \"Implementation-Title\": \"${project.name}\",\n                \"Implementation-Version\": \"${project.mod_version}\",\n                \"Implementation-Vendor\" :\"rubensworks\",\n                \"Implementation-Timestamp\": new Date().format(\"yyyy-MM-dd'T'HH:mm:ssZ\")\n        ])\n    }\n}\n\njavadoc {\n    options.encoding = 'UTF-8'\n    source = [sourceSets.main.allJava]\n    // prevent java 8's strict doclint for javadocs from failing builds\n    options.addStringOption('Xdoclint:accessibility,html,reference,syntax', '-quiet')\n}\n\ntask apiJar(type: Jar) {\n    from (sourceSets.main.output) {\n        include \"**/api/**/*\"\n    }\n    includeEmptyDirs = false\n    archiveClassifier.set('api')\n}\n\ntask sourcesJar(type: Jar) {\n    duplicatesStrategy(DuplicatesStrategy.FAIL)\n    archiveClassifier.set('sources')\n    from sourceSets.main.allJava\n}\n\ntask javadocJar(type: Jar, dependsOn: javadoc) {\n    duplicatesStrategy(DuplicatesStrategy.FAIL)\n    archiveClassifier.set('javadoc')\n    from javadoc.destinationDir\n}\n\nshadowJar {\n    configurations = [project.configurations.shadow]\n    archiveClassifier.set('') // Replace the default JAR\n    append 'META-INF/neoforge.mods.toml'\n    relocate 'com.google.re2j', 'vendors.com.google.re2j' // Because Forge blocks all com.google.* class loading...\n    // To avoid clashes with other mods\n    relocate 'io.netty.handler.codec.http', 'org.cyclops.integrateddynamics.vendors.io.netty.handler.codec.http'\n    relocate 'io.netty.handler.codec.rtsp', 'org.cyclops.integrateddynamics.vendors.io.netty.handler.codec.rtsp'\n    relocate 'io.netty.handler.codec.spdy', 'org.cyclops.integrateddynamics.vendors.io.netty.handler.codec.spdy'\n}\nassemble.dependsOn shadowJar\njar {\n    shadowJar {}\n}\n\nartifacts {\n    archives shadowJar\n\tarchives apiJar\n    archives sourcesJar\n    archives javadocJar\n}\n\n// Replace properties in files\nprocessResources {\n    def expandProps = [\n            'mod_version'                           : project.version,\n            'group'                                 : project.group, //Else we target the task's group.\n            'minecraft_version'                     : minecraft_version,\n            'mod_name'                              : mod_name,\n            'mod_author'                            : mod_author,\n            'mod_id'                                : mod_id,\n            'license'                               : license,\n            'issue_tracker_url'                     : issue_tracker_url,\n            'display_url'                           : display_url,\n            'description'                           : project.description,\n            'neoforge_version'                      : neoforge_version,\n            'neoforge_loader_version_range'         : neoforge_loader_version_range,\n            'neoforge_update_json_url'              : neoforge_update_json_url,\n            'java_version'                          : java_version,\n            'cyclopscore_version'                   : cyclopscore_version,\n            'cyclopscore_version_semver'            : cyclopscore_version.replaceAll(\"-.*\\$\", \"\"),\n            'commoncapabilities_version'            : commoncapabilities_version,\n            'commoncapabilities_version_semver'     : commoncapabilities_version.replaceAll(\"-.*\\$\", \"\")\n    ]\n\n    filesMatching(['pack.mcmeta', 'META-INF/mods.toml', 'META-INF/neoforge.mods.toml', 'mixins.*.json']) {\n        expand expandProps\n    }\n    inputs.properties(expandProps)\n}\n\ntask publishCurseForge(type: TaskPublishCurseForge) {\n    dependsOn(tasks.jar)\n    apiToken = secrets.curseforgeKey;\n    def mainFile = upload(project.curseforge_project_id, shadowJar)\n    mainFile.releaseType = secrets.build_number.equals(\"RELEASE\") ? Constants.RELEASE_TYPE_RELEASE : Constants.RELEASE_TYPE_BETA\n    mainFile.changelogType = \"markdown\"\n    mainFile.changelog = secrets.changelog\n    mainFile.addJavaVersion(\"Java ${project.java_version}\")\n    mainFile.addGameVersion(project.minecraft_version)\n    mainFile.addModLoader(\"NeoForge\")\n\n    mainFile.addRequirement(\"cyclops-core\")\n    mainFile.addRequirement('common-capabilities')\n}\n\nmodrinth {\n    token = secrets.modrinthKey;\n    projectId = \"${modrinth_project_id}\"\n    versionNumber = project.minecraft_version + '-' + project.version\n    versionName = \"${project.version} for NeoForge ${project.minecraft_version}\"\n    versionType = secrets.build_number.equals(\"RELEASE\") ? \"release\" : \"beta\"\n    uploadFile = shadowJar\n    gameVersions = [ project.minecraft_version ]\n    changelog = provider { secrets.changelog }\n\n    dependencies {\n        required.project \"Z9DM0LJ4\" // Cyclops Core\n        required.project \"oFXrCkDI\" // Common Capabilities\n    }\n}\n\npublishing {\n    repositories {\n        if (project.hasProperty(\"filesmaven_url\")) {\n            logger.info('Publishing to files server')\n            maven {\n                url project.filesmaven_url\n                credentials {\n                    username = project.filesmaven_username\n                    password = project.filesmaven_key\n                }\n            }\n        } else if (System.getenv(\"MAVEN_URL\")) {\n            logger.info('Publishing to files server')\n            maven {\n                url System.getenv(\"MAVEN_URL\")\n                credentials {\n                    username = System.getenv(\"MAVEN_USERNAME\")\n                    password = System.getenv(\"MAVEN_KEY\")\n                }\n            }\n        } else {\n            logger.error('No maven repo was set!')\n        }\n    }\n\n    publications { PublicationContainer publicationContainer ->\n        publicationContainer.register(\"maven\", MavenPublication) { MavenPublication publication ->\n            publication.artifacts = [shadowJar, javadocJar, sourcesJar, jar]\n            publication.artifactId = project.base.archivesName.get().toLowerCase() // GH can't handle uppercase...\n        }\n    }\n}\n\nidea {\n    module {\n        downloadSources = true\n        downloadJavadoc = true\n        for (String excludeDirName in [\"run\", \"out\", \"logs\", \"gradle\"]) {\n            File excludeDir = new File(projectDir, excludeDirName)\n            excludeDirs.add(excludeDir)\n        }\n    }\n}\n\njacocoTestReport {\n    // Include unit and game test coverage\n    executionData fileTree(project.buildDir).include(\"jacoco/*.exec\")\n    reports {\n        xml.required = true\n        html.required = true\n    }\n}\n\n// Also measure coverage for game tests\njacocoTestReport.dependsOn test, runGameTestServer\njacoco {\n    toolVersion = \"0.8.14\"\n    applyTo runGameTestServer\n}\n\nspotless {\n    java {\n        target 'src/*/java/org/cyclops/**/*.java'\n\n        endWithNewline()\n        trimTrailingWhitespace()\n        removeUnusedImports()\n        indentWithSpaces()\n    }\n}\n\ntasks.register('updateGitHooks', Copy) {\n    from './scripts/pre-commit'\n    into './.git/hooks'\n}\ncompileJava.dependsOn updateGitHooks\n\ntasks.withType(JavaCompile).configureEach {\n    options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation\n}\n"
  },
  {
    "path": "crowdin.yml",
    "content": "project_id_env: CROWDIN_PROJECT_ID\napi_token_env: CROWDIN_PERSONAL_TOKEN\nbase_path: .\n\npreserve_hierarchy: true\n\nfiles:\n  - source: src/main/resources/assets/integrateddynamics/lang/en_us.json\n    translation: src/main/resources/assets/integrateddynamics/lang/%locale_with_underscore%.json\n    excluded_target_languages:\n      - en\n    skip_untranslated_strings: true\n    languages_mapping:\n        locale_with_underscore:\n            af: af_za\n            ar: ar_sa\n            ca: ca_es\n            cs: cs_cz\n            da: da_dk\n            de: de_de\n            el: el_gr\n            en-US: en_us\n            es-ES: es_es\n            fi: fi_fi\n            fr: fr_fr\n            he: he_il\n            hu: hu_hu\n            it: it_it\n            ja: ja_jp\n            ko: ko_kr\n            nl: nl_nl\n            \"no\": no_no\n            pl: pl_pl\n            pt-BR: pt_br\n            pt-PT: pt_pt\n            ro: ro_ro\n            ru: ru_ru\n            sr: sr_sp\n            sv-SE: sv_se\n            tr: tr_tr\n            uk: uk_ua\n            vi: vi_vn\n            zh-CN: zh_cn\n            zh-TW: zh_tw\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.3.0-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project\nmod_version=1.33.0\ngroup=org.cyclops.integrateddynamics\njava_version=25\n\n# Common\nminecraft_version=26.1.2\nmod_name=IntegratedDynamics\nmod_author=rubensworks (aka kroeser)\nmod_id=integrateddynamics\nlicense=MIT\ngithub_url=https://github.com/CyclopsMC/IntegratedDynamics\nissue_tracker_url=https://github.com/CyclopsMC/IntegratedDynamics/issues\ndisplay_url=https://www.curseforge.com/minecraft/mc-mods/integrated-dynamics\ndescription=Take full and automated control of your appliances.\nfingerprint=bd0353b3e8a2810d60dd584e256e364bc3bedd44\n\ncurseforge_project_id=236307\nmodrinth_project_id=yYzdQHJI\n\n# NeoForge\nneoforge_version=26.1.2.22-beta\nneoforge_loader_version_range=[4,)\nneoforge_update_json_url=https://raw.githubusercontent.com/CyclopsMC/Versions/master/neoforge_update/integrated-dynamics.json\n\n# Gradle\norg.gradle.jvmargs=-Xmx3G\norg.gradle.daemon=false\norg.gradle.caching=true\n\n# Dependencies\ncyclopscore_version=1.29.5-1012\nintegrateddynamicscompat_version=26.1.2-neoforge:1.0.0-168\ncommoncapabilities_version=2.11.4-344\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright  2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions $var, ${var}, ${var:-default}, ${var+SET},\n#           ${var#prefix}, ${var%suffix}, and $( cmd );\n#         * compound commands having a testable exit status, especially case;\n#         * various built-in commands including command, set, and ulimit.\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=${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} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\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\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.8.0 or higher.\n\nAdditions:\n* Add priorities to parts\n* Add read and write aspects for Refined Storage. Read any kind of information from RS networks, or even trigger new crafting jobs.\n* Optimizations in network ticking\n* Optimizations in cable/part rendering\n* Add Portable Logic Programmer\n* Add Forge Energy read aspects and operators\nThe RF api compat has been disabled by default, only players who already had a config file from a previous version will still have the RF compat. (Obviously, it can still be enabled from the config file)\n\nChanges:\n* Simplification of network/part API using capabilities for addons\n* Improve part textures, by Yulife\n* Let batteries emit energy to all sides when a redstone signal is applied\n* Cache cable models to improve render efficiency\n* Increase default meneglin biome and wild menril tree spawn rates\n* Improve understandability of diagnostics ticktimes\n* Allow empty variable cards to be used as True boolean\n* Limit battery I/O energy rates\n* Improve part settings gui\n* Make energy battery energy tick rate configurable\n\nFixes:\n* Fix diagnostics not opening in SMP\n* Fix opaque facades not rendering connected sides\n* Fix facade quad rendering sometimes appearing behind cables, Closes #70\n* Fix Logic Programmer not always correctly labeling, Closes #71\n* Fix aspect values being too big for gui boxes\n* Fix parts dropping when breaking in creative mode\n* Fix incorrect RF interface cast causing an exception when reading\n* Fix parts sometimes not being found\n* Correctly emit FE from batteries and generators\n* Re-init network when removing a part inbetween cables\n* Fix MCMP parts dropping in creative mode\n* Return default aspect property values on corrupted part states\n* Avoid crash when checking for non-existing aspect in a part\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.8.0 or higher.\n\nFixes:\n* Fix RS list proxies not correctly persisting network master positions\n* Fix rare crash when updating redstone info on remapped blocks\n* Fix versionstats url\n\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.3 or higher.\n\nFixes:\n* Fix instant reloading of invalidated network elements, which could cause network crashes\n* Fix networks for negative dimension ids breaking after world restart\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.0 or higher.\n\nFixes:\n* Fix energy transfer happening client-side, Closes #160\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.1 or higher.\n\nFixes:\n* Add better energy support for machines not supporting FE, CyclopsMC/IntegratedTunnels#14\n* Fix incorrect Energy Battery blockstate when energy level becomes negative, Closes #171\n* Fix redstone writers block-updating incorrect side, Closes #170\n* Fix Energy Battery showing RF energy stored as max storable energy, Closes #169\n* Fix crash when breaking part and opening its gui during server lag, Closes #168\n* Fix rare crash for collidable components in cable, Closes #166\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.8.0 or higher.\n\nChanges:\n* Update RS compat for version 1.2\n* Improve type flexibility of greater and less than, Closes #77\n\nFixes:\n* Fix Menril Leaves keeping meta value when silked, Closes #76\n* Fix cable duplication, Closes #78\n* Block invalid items from going into variable store, Closes #79\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.8.0 or higher.\n\nFixes:\n* Allow wild menril tree spawns to be disabled, Closes #82. It does not crash anymore when setting wildMenrilTreeChance to 0.\n* Fix equality of blocks only checking metadata, Closes #83. This did not check equality of the actual block before.\n\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.8.2 or higher.\n\nFixes:\n* Improve facade item rendering, Closes #89\n* Fix network crash for when checking priorities for invalid parts, Closes #88\n* Improve temporary gui data storage to account for latencies, #64\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.8.0 or higher.\n\nFixes:\n* Don't remove blocks for invalid ray traces during block removal, #90\n\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.0 or higher.\n\nAdditions:\n* Added category tooltips\n* Added localization for configuration gui\n* Added config GUI restriction\n\nFixes:\n* Fix crash on invalid variables in variable-driven parts\n* Fix oredict operator returning string instead of list\n* Prevent player being pushed off squeezer and basin, Closes #95\n* Don't try to place cable when using an offset, Closes #93\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.1 or higher.\n\nAdditions:\n* Add Menril Stone Torch, Closes #114\n* Add recipe for clearing part id's in a crafting grid, Closes CyclopsMC/IntegratedTunnels#13\n\nChanges:\n* Add 'torch' oredict entries for Menril Torches\n* Improve manual item collecting from Squeezer and Drying Basin\n\nFixes:\n* Fix Energy Battery causing too many unneeded block updates, Closes #117\n* Fix cable not always visually connecting when placing on a part, Closes #102\n* Fix bucket fill and empty sounds not being heard by the player, Closes #106\n* Fix wrench not rotating all blocks, Closes #107\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.1 or higher.\n\nChanges:\n* Remove invalid number casting, Closes #123. This was introduced because of #15, but this was not required after all.\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.6.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.2 or higher.\n\nAdditions:\n* Add aspect setting to enable strong power in redstone writer, Closes #152\n* Add operator to check if a player has an external gui open, Closes #139\n* Add list head and tail operators, #116\n* Add Immersive Engineering squeezer recipe for Menril Resin\n* Add lazy list builder operator, #116\n* Add list append operator, #116\n* Add list count operators, Closes #137\n* Add cansmelt and canburn operators, #131\n* Add NAND and NOR operators, #131\n\nChanges:\n* Simplify list contains predicate operator\n* Add usage information to Squeezer tooltip, Closes #130\n* Rename Named operator to To String, #124\n\nFixes:\n* Fix lighting not updating when adding/removing facades, #149\n* Fix dark display contents rendering when a facade is present, Closes #149\n* Fix facade rendering on writer parts\n* Make network element adding, removing and ticking thread-safe, Closes #148\n* Fix breaking facade also breaks cable, #148\n* Fix invalid Forestry backpack registration, #154\n* Fix chunk unloading causing part containers with null networks\n* Fix list append operator serialization\n* Fix redstone reader not reading redstone wires in all cases, Closes #135\n* Fix network resets calling add/remove listeners in the wrong order\n* Fix cable dupe when breaking a part with other parts present, Closes #128\n* Fix settings gui doesn't open on server, Closes #64\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.3 or higher.\n\nAdditions:\n* Add 'On the Dynamics of Integration', an in-game guide to the mod, which also contains achievement-based tutorials.\n* Add achievements\n* Add MineTweaker support for Drying Basin and Squeezer\n* Support dynamic item list creation in the Logic Programmer\n* Add Crystalized Menril Block/Brick and Menril Plank Stairs\n* Add Delayer block. It allows you to maintain a relative history of values.\n* Add operators:\n  * slice\n  * uniq\n  * plant-related operators\n  * shearable block and entity\n  * breeding-related entity operators\n  * entity age\n* Add config option to change max network energy transfer rate\n* Allow JEI recipe transfers to Logic Programmer elements with slots.\n* Make Energy Battery capacity increasable by combining them in a crafting grid\n\nChanges:\n* Improve block and item value type rendering in world\n* Make part loaded checks safer, Closes CyclopsMC/IntegratedTunnels#29\n* Move value types before operators in logic programmer\n* Allow null items, blocks and fluids to be defined, Closes #133\n* Expose generator slots for piping, Closes #174\n* Add check to disallow positioned network elements to interact with themselves\n* Properly scale values in variable-activatable tile gui's\n\nFixes:\n* Fix energy transfer happening client-side, Closes #160\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.3 or higher.\n\nFixes:\n* Fix part aspect settings not being restored on world load\n\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.4 or higher.\n\nChanges:\n* Make proxies and delays stackable and clearable, Closes #181\n* Clarify infobook instruction on operator creation, #181\n\nFixes:\n* Fix LP element activation using hotkeys not working\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.5 or higher.\n\nChanges:\n* Update zh_CN file\n* Clarify need of operator variables in infobook, Closes #190\n\nFixes:\n* Fix CME when changing part priorities, Closes CyclopsMC/IntegratedTunnels#34\n* Fix crash when setting filledMenrilLogChance to zero, Closes #188\n* Fix Menril Trees not spawning when Meneglin Biome is disabled, #186\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nChanges:\n* Update zh_CN.lang\n\nFixes:\n* Fix localization entry for wrongCurryingType, Closes CyclopsMC/IntegratedTunnels#36\n* Fix incorrect logicProgrammerOpenRename key localization, Closes #198\n* Fix Minetweaker JEI integration, Closes #187\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nChanges:\n* Improve entity value storage, Closes #201. We now use persistent entity UUID's to refer to entities, so that references to entities persist after world restarts.\n* Improve chance-related config descriptions, Closes #202\n\nFixes:\n* Fix localization error with pipe operator, Closes #39\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nChanges:\n* Add out-of-energy tooltip when parts are disabled, Closes #208\n\nFixes:\n* Add additional debug information on PartStateExceptions, #204\n* Fix Redstone Writer achievement not obtaining when using the Boolean aspect, Closes #207\n* Fix broken Materializers not stacking, Closes #206\n* Catch infinite recursion in operation variables\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nFixes:\n* Forcefully invalidate parts that throw a PartStateException, Closes #204\n\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.7 or higher.\n\nAdditions:\n* Add apply2 and apply3 convenience operators\n* Add operator to get total item count from a list, Closes #220\n* Add operators to get block and item by name, Closes #212\n* Add list concat operator\n\nFixes:\n* Fix entity integration tests due to entity lookup by UUID\n* Fix pipe operator not correctly applying/currying operators, Closes CyclopsMC/IntegratedTunnels#37\n* Fix crash when getting list elements from an empty delay list, Closes #223\n* Fix binary operators appearing twice in the infobook, Closes #218\n* Fix Minetweaker support not handling JEI recipes, Closes #187\n"
  },
  {
    "path": "resources/changelog/1.10.2-0.7.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nFixes:\n* Fix parts not being removed from network when alternatively broken. Closes #232 and closes #240\n* Fix collidable components crash when part container is not found, Closes #241\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.4 or higher.\n\nFixes:\n* Fix instant reloading of invalidated network elements, which could cause network crashes\n* Fix networks for negative dimension ids breaking after world restart\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.0 or higher.\n\nFixes:\n* Fix energy transfer happening client-side, Closes #160\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.1 or higher.\n\nAdditions:\n* Restore IC2 mod compat\n\nChanges:\n\nFixes:\n* Add better energy support for machines not supporting FE, CyclopsMC/IntegratedTunnels#14\n* Fix incorrect Energy Battery blockstate when energy level becomes negative, Closes #171\n* Fix redstone writers block-updating incorrect side, Closes #170\n* Fix Energy Battery showing RF energy stored as max storable energy, Closes #169\n* Fix crash when breaking part and opening its gui during server lag, Closes #168\n* Fix rare crash for collidable components in cable, Closes #166\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.5.txt",
    "content": "Requires CyclopsCore version 0.9.0 or higher.\n\nInitial 1.11 port"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.1 or higher.\n\nAdditions:\n* Add Menril Stone Torch, Closes #114\n* Add recipe for clearing part id's in a crafting grid, Closes CyclopsMC/IntegratedTunnels#13\n* Added category tooltips\n* Added localization for configuration gui\n* Added config GUI restriction\n\nChanges:\n* Add 'torch' oredict entries for Menril Torches\n* Improved item collecting from Squeezer and Drying Basin\n\nFixes:\n* Fix Energy Battery causing too many unneeded block updates, Closes #117\n* Fix incorrect Menril Torch recipe, Closes #115\n* Fix cable not always visually connecting when placing on a part, Closes #102\n* Fix recipes still using null ItemStacks\n* Fix bucket fill and empty sounds not being heard by the player, Closes #106\n* Fix wrench not rotating all blocks, Closes #107\n* Fix oredict recipe entries being incorrect, #108\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.2 or higher.\n\nChanges:\n* Restore Forestry mod compat\n* Remove invalid number casting, Closes #123 This was introduced because of #15, but this was not required after all.\n\nFixes:\n* Fix breakage of generic unshaped recipes, Closes #121\n* Fix localization of LP keybindings, Closes #118\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.6.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.9.3 or higher.\n\nAdditions:\n* Add aspect setting to enable strong power in redstone writer, Closes #152\n* Add operator to check if a player has an external gui open, Closes #139\n* Add list head and tail operators, #116\n* Add Immersive Engineering squeezer recipe for Menril Resin\n* Add lazy list builder operator, #116\n* Add list append operator, #116\n* Add list count operators, Closes #137\n* Add cansmelt and canburn operators, #131\n* Add NAND and NOR operators, #131\n* Add usage information to Squeezer tooltip, Closes #130\n\nChanges:\n* Simplify list contains predicate operator\n* Rename Named operator to To String, #124\n\nFixes:\n* Fix Drying Basin recipes sometimes breaking, Closes #156\n* Fix Drying Basin rendering missing texture'd particles\n* Fix lighting not updating when adding/removing facades, #149\n* Fix dark display contents rendering when a facade is present, Closes #149\n* Fix facade rendering on writer parts\n* Make network element adding, removing and ticking thread-safe, Closes #148\n* Fix breaking facade also breaks cable, #148\n* Fix Forestry mod compat crash, Closes #154\n* Fix invalid Forestry backpack registration, #154\n* Fix chunk unloading causing part containers with null networks\n* Fix list append operator serialization\n* Fix redstone reader not reading redstone wires in all cases, Closes #135\n* Fix network resets calling add/remove listeners in the wrong order\n* Fix settings missing texture and localization, Closes #127\n* Update Refined Storage API, Closes #132\n* Fix cable dupe when breaking a part with other parts present, Closes #128\n* Fix settings gui doesn't open on server, Closes #64\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.3 or higher.\n\nAdditions:\n* Add 'On the Dynamics of Integration', an in-game guide to the mod, which also contains achievement-based tutorials.\n* Add achievements\n* Support dynamic item list creation in the Logic Programmer\n* Add Crystalized Menril Block/Brick and Menril Plank Stairs\n* Add Delayer block. It allows you to maintain a relative history of values.\n* Add operators:\n  * slice\n  * uniq\n  * plant-related operators\n  * shearable block and entity\n  * breeding-related entity operators\n  * entity age\n  * item inventory-related operators\n* Add config option to change max network energy transfer rate\n* Allow JEI recipe transfers to Logic Programmer elements with slots.\n* Make Energy Battery capacity increasable by combining them in a crafting grid\n\nChanges:\n* Restore TCon compat\n* Improve block and item value type rendering in world\n* Make part loaded checks safer, Closes CyclopsMC/IntegratedTunnels#29\n* Move value types before operators in logic programmer\n* Allow null items, blocks and fluids to be defined, Closes #133\n* Expose generator slots for piping, Closes #174\n* Add check to disallow positioned network elements to interact with themselves\n* Properly scale values in variable-activatable tile gui's\n\nFixes:\n* Fix energy transfer happening client-side, Closes #160\n* Fix cansmelt operator always returning true\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.3 or higher.\n\nFixes:\n* Fix part aspect settings not being restored on world load\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.4 or higher.\n\nChanges:\n* Make proxies and delays stackable and clearable, Closes #181\n* Clarify infobook instruction on operator creation, #181\n\nFixes:\n* Fix LP element activation using hotkeys not working\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.5 or higher.\n\nChanges:\n* Clarify need of operator variables in infobook, Closes #190\n* Update zh_CN file\n\nFixes:\n* Fix CME when changing part priorities, Closes CyclopsMC/IntegratedTunnels#34\n* Fix crash when setting filledMenrilLogChance to zero, Closes #188\n* Fix Menril Trees not spawning when Meneglin Biome is disabled, #186\n* Fix NPE when filtering lists of items, Closes #185\n* Fix variable card localized tooltip\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nAdditions:\n* Restore Immersive Engineering compat\n* Restore Minetweaker compat\n\nChanges:\n* Update zh_CN.lang\n\nFixes:\n* Fix TConstruct compat not being registered\n* Fix localization entry for wrongCurryingType, Closes CyclopsMC/IntegratedTunnels#36\n* Fix incorrect logicProgrammerOpenRename key localization, Closes #198\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nAdditions:\n\nChanges:\n* Move mod compats to a separate jar which is shadowed in the main jar dynamically\n\nFixes:\n* Fix Logic Programmer input type tooltips not showing, Closes #200\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nChanges:\n* Improve chance-related config descriptions, Closes #202\n\nFixes:\n* Improve entity value storage, Closes #201. We now use persistent entity UUID's to refer to entities, so that references to entities persist after world restarts.\n* Fix localization error with pipe operator, Closes #39\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.7.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nAdditions:\n\nChanges:\n* Add out-of-energy tooltip when parts are disabled, Closes #208\n* Reduce number of cable connection updates to the bare minimum. Not all sides always need to be updated, only those sides that had a cable connection.\n* Add additional debug information on PartStateExceptions, #204\n\nFixes:\n* Fix Redstone Writer achievement not obtaining when using the Boolean aspect, Closes #207\n* Fix broken Materializers not stacking, Closes #206\n* Catch infinite recursion in operation variables\n\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.8.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nAdditions:\n* Add Liquid and Crystalized Chorus\n* Add Mono-Directional Connector\n* Add Omni-Directional Connector\n\nFixes:\n* Fix entity display client-side crash\n* Fix Drying Basin appendix localization\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.8.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.6 or higher.\n\nFixes:\n* Fix Lombok being packaged\n* Forcefully invalidate parts that throw a PartStateException, Closes #204\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.8.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.7 or higher.\n\nAdditions:\n* Add apply2 and apply3 convenience operators\n* Add operator to get total item count from a list, Closes #220\n* Add operators to get block and item by name, Closes #212\n* Add list concat operator\n\nFixes:\n* Fix entity integration tests due to entity lookup by UUID\n* Fix pipe operator not correctly applying/currying operators, Closes CyclopsMC/IntegratedTunnels#37\n* Fix crash when getting list elements from an empty delay list, Closes #223\n* Fix binary operators appearing twice in the infobook, Closes #218\n* Fix Minetweaker support not handling JEI recipes, Closes #187\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.8.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.7 or higher.\n\nFixes:\n* Fix some operators returning null itemstacks\n* Fix item frame aspect returning a null itemstack\n* Fix display panel not properly updating when reading inventory slot values\n* Fix crash on empty inventory slot reading\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.8.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nChanges:\n* Update Chorus related textures\n\nFixes:\n* Fix parts not being removed from network when alternatively broken. Closes #232 and closes #240\n* Fix collidable components crash when part container is not found, Closes #241\n"
  },
  {
    "path": "resources/changelog/1.11.2-0.8.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nFixes:\n* Fix part guis not openable after network regaining power, Closes #254\n* Allow swapping of items in writers when clicking with variables, Closes #248\n* Avoid chunk updates when updating redstone writer signal, Closes #247\n  This also reduces the number of neighbours that get triggered to the absolute minimum.\n"
  },
  {
    "path": "resources/changelog/1.12-0.7.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nInitial 1.12 port"
  },
  {
    "path": "resources/changelog/1.12-0.7.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nChanges:\n* Improve chance-related config descriptions, Closes #202\n* Disable config version warning\n\nFixes:\n* Improve entity value storage, Closes #201. We now use persistent entity UUID's to refer to entities, so that references to entities persist after world restarts.\n* Fix localization error with pipe operator, Closes #39\n"
  },
  {
    "path": "resources/changelog/1.12-0.7.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nChanges:\n* Add additional debug information on PartStateExceptions, #204\n* Add out-of-energy tooltip when parts are disabled, Closes #208\n* Reduce number of cable connection updates to the bare minimum. Not all sides always need to be updated, only those sides that had a cable connection.\n\nFixes:\n* Fix Redstone Writer achievement not obtaining when using the Boolean aspect, Closes #207\n* Fix broken Materializers not stacking, Closes #206\n* Catch infinite recursion in operation variables\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nAdditions:\n* Add Liquid and Crystalized Chorus\n* Add Mono-Directional Connector\n* Add Omni-Directional Connector\n\nFixes:\n* Fix entity display client-side crash\n* Fix Drying Basin appendix localization\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.8 or higher.\n\nFixes:\n* Fix Lombok being packaged\n* Forcefully invalidate parts that throw a PartStateException, Closes #204\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.10 or higher.\n\nChanges:\n* Update recipe id references due to latest CyclopsCore changes\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.11 or higher.\n\nAdditions:\n* Add apply2 and apply3 convenience operators\n* Add operator to get total item count from a list, Closes #220\n* Add operators to get block and item by name, Closes #212\n* Add list concat operator\n\nFixes:\n* Fix Meneglin Discovery advancement not being obtainable, Closes #228\n* Fix entity integration tests due to entity lookup by UUID\n* Fix pipe operator not correctly applying/currying operators, Closes CyclopsMC/IntegratedTunnels#37\n* Fix crash when getting list elements from an empty delay list, Closes #223\n* Fix binary operators appearing twice in the infobook, Closes #218\n* Fix Minetweaker support not handling JEI recipes, Closes #187\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.12 or higher.\n\nAdditions:\n* Allow text fields to become fully selected on right-click\n\nChanges:\n* Mention delayer and materializer variable card restrictions in infobook\n\nFixes:\n* Fix variable transformer advancements not being obtainable, Closes #231\n* Fix some operators returning null itemstacks\n* Fix broken target entity operator\n* Fix item frame aspect returning a null itemstack\n* Fix display panel not properly updating when reading inventory slot values\n* Fix breaking integration tests\n* Fix crash on empty inventory slot reading\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.13 or higher.\n\nChanges:\n* Allow air block references to be created inside the LP, Closes #249\n* Increase max width of labels in writer guis\n* Allow aspect property tooltips to be added\n* Allow default update intervals of part states to be changed\n* Make enriched menril logs silk touchable\n* Update Chorus related textures\n\nFixes:\n* Fix incorrect itemstack valuetype null check\n* Fix parts not being removed from network when alternatively broken. Closes #232 and closes #240\n* Fix collidable components crash when part container is not found, Closes #241\n* Fix squeezer losing fluid NBT tag information\n* Fix some operators returning null itemstacks\n* Fix item frame aspect returning a null itemstack\n* Fix display panel not properly updating when reading inventory slot values\n* Fix crash on empty inventory slot reading\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.14 or higher.\n\nFixes:\n* Fix part guis not openable after network regaining power, Closes #254\n* Allow swapping of items in writers when clicking with variables, Closes #248\n* Avoid chunk updates when updating redstone writer signal, Closes #247\n  This also reduces the number of neighbours that get triggered to the absolute minimum.\n"
  },
  {
    "path": "resources/changelog/1.12-0.8.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.16 or higher.\n\nChanges:\n* Make Energy Battery level rendering TESR-based, Closes #258\n  This avoids having to send block updates on energy level changes,\n  which improves FPS, but also improves energy level rendering.\n\nFixes:\n* Fix variable store contents sometimes not being synced with network, Closes #239\n* Fix format errors on some logic programmer symbols\n* Fix infinite loop when Delayer capacity is set to 0, Closes #263\n* Fix crash when reading entity inventories in the nether, Closes #260\n* Make compareTo implementations safer for clashes\n  Some implementations were done using hashCode, which could result in clashes for non-equal objects.\n  This could lead to non-equal objects being evicted from collections such as sets.\n  This can explain the bug where networks don't revalidate certain parts in the network after reconstruction due to other parts clashing with them.\n  Ignored parts such as writers would not have their `PartTypeWriteBase#updateActivation` method called, which would case their internal selected aspect to remain `null`,\n  which would lead to an inactivated part that shows an error due to it still having an item in its inventory. #259\n* Fix client crash when reconnecting cables using a wrench\n* Fix Squeezer inventory hash not resetting when height is updated\n  Closes CyclopsMC/IntegratedTunnels#52\n  This fixes the problem where IT item transfer was sometimes delayed for insertion into the Squeezer\n* Fix incorrect offhand handling when wrenching, Closes #257\n* Fix generator animation not stopping when finished, Closes #256\n"
  },
  {
    "path": "resources/changelog/1.12-0.9.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.16 or higher.\n\nAdditions:\n* Add NBT value type\n* Add NBT operators\n* Add block reader aspect to get the NBT tag of a tile entity\n\nChanges:\n* Make list get operator throw out-of-bounds error and add safe variant, Closes #265\nPreviously, the 'get' operator would simply return the default value of the value type if the index was out of bounds.\nThis caused to many issues, in cases where the list type was 'any' for example.\n\nFixes:\n* Fix display panels causing unnecessary chunk rerenders\n* Fix some type-safety issues\n"
  },
  {
    "path": "resources/changelog/1.12-0.9.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.18 or higher.\n\nAdditions:\n* Restore CraftTweaker integration\n* Restore TCon integration\n\nChanges:\n* Make menril wood smelt into charcoal\n* Rename some blocks/items per #272\n  * Rename \"Menril Log\" to \"Menril Wood\"\n  * Rename \"Enriched Menril Log\" to \"Enriched Menril Wood\"\n  * Rename \"Menril Plank\" to \"Menril Wood Planks\"\n  * Rename \"Menril Plank Stairs\" to \"Menril Wood Stairs\"\n* Allow NBT keys to be blacklisted from reading, Closes #271\n\nFixes:\n* Localize sound event subtitles, Closes #277\n* Fix network crash when uninitialized part is initialized later, Closes #282\n* Fix certain aspects producing null ItemStacks, Closes CyclopsMC/IntegratedTunnels#56\n"
  },
  {
    "path": "resources/changelog/1.12-0.9.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.19 or higher.\n\nAdditions:\n* Add operator to get an operator by name, Closes #287\n* Allow name to be retrieved from operator values, #287\n\nChanges:\n\nFixes:\n* Fix incorrect markDirty() call when writing tiles to NBT, Closes #286\n  This could be the cause of rare CMEs during chunk saving.\n* Fix sound operator-related crashes in SMP, Closes #285\n"
  },
  {
    "path": "resources/changelog/1.12-0.9.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.21 or higher.\n\nAdditions:\n* Add operator to get unique names of certain values, Closes #293\n  This includes items, blocks, entities, fluids and operators.\n* Display '\\n' as line breaks in Display Panel, Closes #296\n\nChanges:\n* Use Slimeball oredict entry for facade recipe, Closes #306\n\nFixes:\n* Fix parts losing information if placed without a cable, Closes #303\n* Fix name of operator values being unlocalized, Closes #304\n* Fix incorrect button hover effect in LP on lists\n* Don't generate omnidirectional particles when game is paused, Closes #302\n* Fix invalid localization of invalid containing proxy type error, Closes #298\n* Fix readers and writers being empty in some languages, Closes #294\n* Fix LP list screen becoming empty when screen size is changed\n* Fix items, blocks and fluids not being insertable into Lists in the LP from JEI, Closes #292\n* Fix coal generator consuming empty buckets after draining lava, Closes #305\n* Use reference-based DefaultCapabilityProvider, this resolves some crashes when other mods use items early during init, Closes CyclopsMC/CyclopsCore#80\n* Fix neighbour cables not always rerendering after placing new cable\n* Fix crash when casting delay default value to a number, Closes #289\n* Concretize conditional output type of list get operator with default, this resolves some crashes with lists\n* Fix oredict operator not handling wildcard meta values, Closes #288\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.10.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.0 or higher.\n\nAdditions:\n* Add Mechanical Squeezer, an advanced and automatable version of the Squeezer\n* Add Mechanical Drying Basin, an advanced and automatable version of the Drying Basin\n* Add channels, this is particularly useful for Integrated Tunnels\n* Add network aspect for reading the network consumption rate\n* Add TPS aspect to world and extradimensional readers\n* Add auto-supply mode to batteries\n  This allows you to automatically fill held energy containers.\n* Add operators to read items, fluids and energy from entities, Closes #403\n* Add redstone pulse writing aspects, Closes #418\n* Add part setting for changing the effective target side, #278\n* Add index_of, index_of_regex, regex_groups, replace, replace_regex, matches_predicate.\n* Add Signals compat\n* Add molten metal convenience recipes to Drying Basin\n* Add an \"Is Minecart\" operator\n* Add an entity and machine writer\n  Like the inventory writer, these will only be useful with cross-mod compats\n* Add an offset time to the redstone clock\n  This lets you have, for example, 4 redstone clocks, each one on for a different\n  1/4 of each second. Without this, they'd all be on for the same 1/4 of each\n  second. This partially solves #317; it will fully solve it once #197 is solved.\n* Add a recipe to squeeze menril resin out of planks\n* Add menril doors\n* Allow squeezing of poisonous materials\n\nChanges:\n* Balance default energy capacity of batteries\n  This multiplies the default capacity by a factor of 10, in order to have a better balance compared to other RF storage mods.\n* Make battery transfer limit in relation to its capacity, #377\n* Show outline of variables in variable slots\n* Show battery energy level toast when right clicking with an empty hand\n* Add warning about certain parts not having aspects, Closes #214\n* Give 3 omni-directional connectors instead of 2 when consuming one\n* Don't show empty fluid squeezer slots in infobook\n* Modify several Squeezer recipe outputs to be probabilistic\n\nFixes:\n* Fix missing texture on the bottom of the Materializer\n* Fix part settings button hovering appearing incorrectly, Closes #453\n* Fix crash due to casting ANY to NUMBER\n* Fix JEI recipe clicking in LP overwriting last inventory slot, Closes CyclopsMC/IntegratedDynamics#441\n* Fix incorrect JEI LP recipe fill in target\n* Fix machine deactivation after finishing each recipe\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.10.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.1 or higher.\n\nAdditions:\n* Add safe-mode option, Closes #486\n  In cases where a network would become horribly broken because of a bug,\n  servers could get stuck in a repeated server crash loop.\n  This makes it possible to temporarily set the mod in safe-mode,\n  which means that networks won't tick anymore, values are not evaluated and no special rendering is done.\n  In this mode, the offending cables and parts can be removed.\n  When disabling this option again, everything will work again as normal.\n\nChanges:\n* Make network crash disabler safety listen to all types of crashes, Closes #468\n* Rename frequency setting to 'ticks/operation', Closes #462\n* Make channel handling more efficient\n\nFixes:\n* Fix mechanical machines sometimes not producing nothing, Closes #457\n* Fix dynamic light panels appearing enabled for 0 and false values, Closes #475\n* Fix rare crash when removing while it is being reloaded, Closes #472\n* Fix curried operator result not being usable in choice operator, Closes #484\n* Fix crash when certain mods call client-side code in item localization, Closes #106\n* Validate predicate output type before casting to boolean in filters\n  Static type checking was already being done, but when currying was applied, this issue could sometimes occur.\n  This was the same problem as in CyclopsMC/IntegratedTunnels#99, Closes #487\n* Fix part settings resetting to default values when resizing screen, Closes #455\n* Fix Redstone Writer not always notifying neighbours correctly, Closes #356\n* Add safety to avoid serializing values that are too big for packets\n  This adds the config setting maxValueByteSize that could be adjusted by the player if necessary., Closes #362\n* Fix cable item rendering too dark, Closes #479\n* Fix drying basin and squeezer sending too many updates\n* Fix parts on wildcard channels not including parts from all channels\n* Fix batteries in creative tab not containing a lot of energy\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.10.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.1 or higher.\n\nChanges:\n* Don't save part ID when broken with pickaxe, Closes #493\n  This makes network dismantling a bit more convenient if you don't need the IDs anymore.\n\nFixes:\n* Fix JEI showing chances as 1/100 of their actual value\n* Fix JEI compat breaking when certain blocks/items are disabled\n* Fix crash when disabling mechanical drying basin, Closes #482\n* Fix mech machines not working after chunk reload when network-powered, Closes #489\n* Fix #491: use the right dye metadata for squeezing dandelions\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.10.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.1 or higher.\n\nFixes:\n* Fix redstone writer sometimes not correctly emitting redstone values, Closes #422\n  The problem was that (redstone) capabilities could become rotated so that they would emit redstone at the incorrect side.\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.10.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.1 or higher.\n\nChanges:\n* Rebalance (mechanical) squeezer recipe for blaze rod to powder\n  This fixes a dupe exploit in combination with TE and IC2.\n* Only output one Nether Quartz Dust in Mechanical Squeezer, Closes #499\n  This fixes a Nether Quartz duplication exploit when combined with other mods.\n\nFixes:\n* Allow piped ops to take more inputs if the second operator takes +1 args, Closes #514\n* Fix left key not being usable in LP textboxes\n* Fix type parameter of ListOperatorSerializer\n* Fix numbers not being convertable to strings, Closes #504\n* Fix certain advancements failing to unlock, Closes #500\n  Fixed advancements:\n  * Redstone Observement\n  * Constant Definition\n  * Arithmetic Addition\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.2 or higher.\n\nAdditions:\n* Add NBT union, intersection and minus operators\n* Add operator to check if an NBT tag is a subset of another NBT tag\n* Add a pipe2 operator\n  This operator lets you use an input multiple times. In lambda calculus,\n  it's `\\wxyz.y(wz)(xz)`. An example use of this would be\n  pipe2(Item Max Damage, Item Damage, Arithmetic Subtraction). This creates\n  an operator that tells you the damageability remaining of any given item.\n  Fixes part of #195.\n* Add the K combinator\n\nChanges:\n* Make network-related capability retrieval sided, this enables full compatibility with Compact Machins, Closes #420\n* Make certain aspect only update on block updates\n  Operators that depend on those values will also only update when the value updates.\n  This will result in a major performance improvement for certain networks.\n* Allow custom iteration orders to be passed to positioned addons\n\nFixes:\n* Fix console spam when previewing cables in compact machines\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.2 or higher.\n\nFixes:\nFix partData error showing in TOP, Closes #522\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.5 or higher.\n\nChanges:\n* Compress mechanical_squeezer_gui_jei.png\n\nFixes:\n* Fix Refined Storage autocrafting writer aspects not working, Closes #567\n* Fix Mechanical Squeezer crafttweaker crash\n  An optional was forgotten in 97cafd766e62f929224f6f8a4d6306d501e597cd\n* Fix networks with no active elements not being properly reloaded, Closes CyclopsMC/IntegratedTunnels#124\n* Fix formatting typo in value types book section\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.5 or higher.\n\nAdditions:\n* Restore Thaumcraft integration.\n  This includes aspects and operators to read Thaumcraft aspects from\n  essentia tubes, items and entities.\n  These aspects are available as a separate value type.\n\nFixes:\n* Fix memory leak and stackoverflow due to cloning of positioned iterators, Closes #568\n* Fix crash when mapping lists using an operator that outputs a category, #570\n* Fix crash when applying Flip on an operator with 1 input, Closes #570\n* Fix odd Menril Tree growth in flat worlds, Closes #571\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.6 or higher.\n\nFixes:\n* Fix incorrect regex_groups operators output type, Closes #579\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.6 or higher.\n\nChanges:\n* Update zh_cn.lang\n\nFixes:\n* Fix inexact scaling of energy bars on batteries\n* Make energy handlers safe to integer overflows, Closes #587\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.14.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.7 or higher.\n\nChanges:\n* Add visual indicator in operator dropdown list on next/prev elements, Closes #592\n\nFixes:\n* Safely throw invalid value type errors as ingame errors, Closes #593\n* Fix worldgen crash if filled menril log is disabled, Closes #598\n* Fix crash on null Thaumcraft AspectLists in logic programmer, Closes CyclopsMC/IntegratedDynamics#602\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.15.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.7 or higher.\n\nChanges:\n* Optimizes images\n\nFixes:\n* Fix incomplete flipped operators throwing errors, Closes #609\n* Fix simple omnidirectional copy recipe resetting IDs, Closes #612\n* Fix drying basin and squeezer not updating visual contents client-side\n  This could happen when the contents were changed externally,\n  using a hopper for instance.\n  Closes #610\n* Fix variables with operations not updating after chunk reload, Closes #611\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.16.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.7 or higher.\n\nChanges:\n* Remove charcoal to coal dust squeezer recipes\n  Other mods don't do this either,\n  so this is removed for balancing reasons.\n  Closes #618\n\nFixes:\n* Add state-type check to display panel TESR\n  This fixes a rare crash where swapping dimensions could cause\n  conflicting part states.\n  Closes #619\n\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.17.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.7 or higher.\n\nFixes:\n* Update Refined Storage compat to 1.6.x, Closes #622\n* Fix crash when applying operations on numbers categories, Closes #621\n* Fix proxies and delays not working anymore after being moved, Closes #626\n* Fix cables and blocks reading connection data from items\n* Fix crash when applying isNull on ANY value types, Closes #625\n* Fix rare crash when looking at display panel, Closes #627\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.18.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.7 or higher.\n\nChanges:\n* Reduce number of chunk updates for the Energy Battery\n* Only show copy warning on non-empty variable cards\n\nFixes:\n* Properly implement getBlockFaceShape on cables, Closes #634\n  This fixes things like snow and torches being placeable on all cables.\n* Fix Generator voiding empty buckets, Closes #635\n* Fix potential energy overflows when inserting energy, Closes #639\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.2 or higher.\n\nChanges:\n* Remove UUID when converting entities to strings, Closes #530\n  This makes things less complicated to work with.\n  This UUID in the string is not required anymore as unique names (with UUID) can now be retrieved using a separate operator.\n\nFixes:\n* Fix player unique name including the '(null)' suffix, #530\n* Fix rare crash when rendering Energy Battery, Closes #529\n* Fix pipe operator announcing incorrect types, Closes #523\n  This was a regression because of 396da75c8c84721e37d6d925247e398684690ebe\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.4 or higher.\n\nAdditions:\n* Add human-readable Display Panel view for Operators, Closes #515\n\nChanges:\n* Remove write button in aspect properties, Closes #379\n  Aspect properties are now saved when the gui is closed,\n  which makes configuring many properties (like in IT) a lot more convenient.\n* Gray out part channel setting if energy is not applicable, CyclopsMC/IntegratedTunnels#109\n\nFixes:\n* Fix LP scrolling bar resetting when pulling item out of result, Closes #532\n* Fix properties of errored aspects not applying, Closes #519\n* Fix Energy Batteries not having break particles, Closes #520\n* Fix positioned iterators possibly becoming invalid in the same tick\n  This was bug introduced because of the new round-robin iterators.\n  This caused loss of items in some cases when using the\n  IT player simulator, CyclopsMC/IntegratedTunnels#111\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.4 or higher.\n\nAdditions:\n* Add copy functionality to the display panel, Closes #534\n  This introduces a new copy button and hotkey to copy the current contents of the display panel to the clipboard of your operating system.\n\nFixes:\n* Fix label overlapping with button in delayer gui\n* Fix values depending on delayer results never updating, Closes #539\n* Fix block reader not updating on all types of block updates, Closes #538\n* Fix crash when incorrectly applying with non-ops as first arg, Closes #537\n* Fix part settings tooltip on channel being disabled always shown, Closes #535\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.4 or higher.\n\nAdditions:\n* Add dimension blacklist options for wild Menril Tree and Meneglin biome, Closes #541\n  This can now be achieved using the `meneglinBiomeDimensionBlacklist` and `wildMenrilTreeDimensionBlacklist` config options.\n\nChanges:\n* Output at most 4 string per wool in (mechanical) squeezer, Closes #542\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.4 or higher.\n\nAdditions:\n\nChanges:\n* Add warning to variable cards regarding copying when in creative-mode\nMiddle-click creative-mode copying causes multiple variable cards with the same ID to exist, which can cause many network issues.\n\nFixes:\n* Fix energy unit tests and creative energy battery appearing not to be full\n* Enhance squeezer recipe JEI display for many outputs\n  This allows either 4 output items to be shown, or 3 items and 1 fluid.\n  CyclopsMC/IntegratedDynamics#548\n* Fix squeezer JEI recipes not always showing chances\n  Closes CyclopsMC/IntegratedDynamics#548\n* Fix Nether Quartz squeezer recipes not working, Closes #550\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.5 or higher.\n\nFixes:\n* Fix round robin iteration not working after working on some elements, Closes CyclopsMC/IntegratedTunnels#119\n* Fix JEI integration failing for LP item variables, Closes CyclopsMC/IntegratedDynamics#561\n* Fix nether quartz squeezer recipes not always being available\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.5 or higher.\n\nFixes:\n* Fix aspect settings not saving after closing gui with escape\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.11.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.11.5 or higher.\n\nFixes:\n* Fix crashing crafttweaker compat due to non-backwards compatible change, Closes CyclopsMC/IntegratedDynamics#563\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.9.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.22 or higher.\n\nAdditions:\n* Allow \\n to be escaped in display panels, Closes #311\n\nChanges:\n* Improve performance of inventory list proxy iteration\n* Losslessly compress all of the PNGs\n* Clarify redstone clock pulse length range, Closes #318\n\nFixes:\n* Fix total fluid capacity aspect functioning incorrectly, Closes #328\n* Fix briefly cable rendering when only placing a part\n* Fix cables being invisible after being replaced on a part, Closes #325\n* Fix neighbour cables not always rerendering on machine place, #325\n* Fix startup warning because of missing model, Closes #313\n* Fix Menril Tree growth replacing all blocks, Closes #316\n* Fix some edge cases where additional Menril Tree drops are not collected, Closes #319\n* Fix #319: override the non-deprecated version of getDrops\n* Fixed typo in book\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.9.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.22 or higher.\n\nChanges:\n* Add distinguishing render effect to Creative Energy Batteries\n* Sign jars\n* Allow opening GUIs without power\n  Once channels become a thing, this will be the only way to fix an incorrect\n  channel. Readers and writers are marked with \"NO POWER\" instead of displaying\n  any useful information. Fixes half of #324.\n* Add entity id to string value of entity values, #338\n  This makes it clearer that entities are stored by id, and equality checks are done based on this id.\n\nFixes:\n* Fix rare Energy Battery render crash, Closes #86\n* Postpone network NBT reading until the first server tick, Closes #349\n  Some other mods, such as RFTools dimensions register their dimensions during the FMLServerStartedEvent,\n  which is when networks are being constructed as well.\n  This can cause network corruption due to some parts thinking they are in a dimension that does not exist,\n  due to that dimension not being registered yet.\n* Catch casting exceptions in NBT list operators, Closes #340\n* Fix Inventory Reader aspect crashes on invalid inventory, Closes #346\n* Fix curseforge URL in mcmod.info\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.9.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.22 or higher.\n\nAdditions:\n* Add operator to get the type of an entity, Closes #358\n  This adds a way to get the old entity information that was possible before ee0c2712f508dbfa9a802aaeb6533a894b038bf5.\n\nChanges:\n* Make the input of round, floor and ceil numbers, Closes #341\n* Make equals input type checks less strict, Closes #365\n* Improve compact names of entity values, #358\n  Before this, player names were always null and entity item names were not pretty.\n* Simplify the redstone clock so that duty cycles > 50% work\n\nFixes:\n* Fix parts being able to transform into cables when placed, Closes #354\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.9.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.24 or higher.\n\nChanges:\n* Allow crafting two omni-directional connectors together to link them\n* Handle infinities better and avoid a temporary array\n\nFixes:\n* Fix crash when creating fluid lists in the LP, Closes #404\n* Fix infinite recursion not always being detected, Closes #369\n* Fix part removal in unloaded chunks, Closes #421\n* Fix cables not connecting when placed without a player, Closes #343\n  This moves network init logic to an earlier phase of block placement,\n  the player-sensitive logic is delayed.\n* Fix LP shift-clicking from output slot going to arg slot, Closes #382\n  This resulted in a loss of variables.\n* Fix cables not being placeable underwater, Closes #373\n* Fix IgnoredBlockStatus only handling valid enums\n  This catches the potential IllegalArgumentException that might get thrown if some-one passes something\n  that isn't a valid enum value in Status, such as \"hello\" into the function.\n  This now matches the behaviour of vanilla minecraft's PropertyInteger.parseValue, which stores it in an\n  internal map (of whose get function never throws).\n* Fix resetting IDs in connectors not working correctly\n* Fix certain stairs being listed as fuel, Closes #426\n* Fix incorrect empty list type on entity inventory operators\n* Fix #397: stop misusing hash codes in uniq and uniq_p to make them retain the original order\n* Fix #392: obfuscate the right soundName\n* Use the right name when giving type errors from NBT operators\n* Fix #331: don't crash over an aspect model not being found\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.9.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.24 or higher.\n\nFixes:\n* Revert \"Clean up equals and add hashCode to Network\"\n  This fixes networks not properly updating after adding parts, or saving\n  This reverts commit 4d5b2c641d655fb079da2006191eeea17f10a061.\n* Fix JEI recipe clicking in LP overwriting last inventory slot, Closes CyclopsMC/IntegratedDynamics#441\n* Fix incorrect JEI LP recipe fill in target\n* Fix TCon compat not being enabled\n* Fix items behaving weirdly when creating lists in the LP, Closes #446\n"
  },
  {
    "path": "resources/changelog/1.12.2-0.9.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.10.24 or higher.\n\nAdditions:\n* Add convenience aspect to fluid reader to read fluid blocks, Closes #405\n\nFixes:\n* Fix crash on comparator reading on certain blocks, Closes #450\n* Fix incorrect LP dropdown rendering order\n  The dropdown of the operators field was sometimes being rendered in front of the LP element type name box.\n* Fix incorrect operator value signatures, Closes #386\n* Fix generator incorrectly internally acting as variable store\n* Fix value updates not being propagated when 1+ steps from a proxy, Closes #374\n* Allow flip operator to be used for operators with 2+ inputs, Closes #387\n* Fix advancement condition logging errors for certain predicate checks\n* Fix strong redstone power not always updating all blocks, #422\n* Fix part states not properly for updating player, Closes #389\n* Fix NBT serialization in ValueTypeListProxyEntityBase not being called, Closes #393\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nAdditions:\n* Add Ingredients value type: a collection of multiple items, fluids and energy.\n* Add Recipe value type: A mapping from Ingredients to Ingredients\n* Add operators to handle recipes\n* Add recipe-reading aspects to Machine Reader\n* Add LP keybindings to infobook\n* Add advancements on recipes and ingredients\n* Add recipe handler capabilities to all recipe-related machines\n* Add JEI transfer handler for recipe LP element\n* Create sv_se.lang\n* Add ingredient observers to network diagnostics\n* Add a reduce1 operator\n  reduce1(op, list) acts like reduce(op, tail(list), head(list)).\n\nChanges:\n* Allow variables in all parts to be referenced\n  All parts basically act as a Variable Store now.\n* Optimize images\n* Allow fluids to be empty in LP\n* Rewrite energy network to an abstract ingredients network (with multi-threaded ingredient observer)\n\nFixes:\n* Fix CraftTweaker recipes ignoring NBT in inputs\n  Closes CyclopsMC/IntegratedDynamics#659\n* Remove PositionsIterator in favor of IPartPosIteratorHandler\n  This resolves many issues with round-robin mode, closes #617.\n* Fix PartPos not always taking into account null sides\n* Fix creative energy battery not being infinite in item form\n  This mirrors what TileEnergyBattery already does.\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nFixes:\n* Fix generator voiding energy instead of inserting into consumers, #671\n* Fix ItemStack deserialization when stack size is larger than 127, Closes #669\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nChanges:\n* Reduce number of chunk-refreshing sendUpdate calls, CyclopsMC/CyclopsCore#40\n* Only send mechanical machine tile data to clients when gui is open\n  This significantly reduces lag when machines\n  are actively working in the background.\n  Closes #630\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nFixes:\n* Fix labels sometimes not syncing anymore to clients\n  This was caused by labels sometimes becoming null.\n  This commit fixes that problem,\n  and includes a fix for existing null labels\n  Closes #712\n* Fix crash when rendering non-item-based recipes\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nAdditions:\n\nChanges:\n* Improve first input determination in recipe handlers\n  Related to CyclopsMC/IntegratedCrafting#30\n* Clarify Reader dependency in Redstone Writer advancement\n  Closes #713\n* Add check for list proxies that have a missing default constructor, #721\n\nFixes:\n* Make JEI recipe wrapper type checking in categories stricter\n  Closes CyclopsMC/IntegratedDynamics#717\n* Fix broken texture when silk touching logic programmer, Closes #728\n* Fix fluid-based recipes matching with all fluids\n  Closes CyclopsMC/IntegratedCrafting#28\n* Allow all items to be inserted into mechanical machines\n  This fixes problems with insertion order of ingredients\n  in Integrated Crafting causing crafting errors, Closes #29\n* Fix all list value proxies that have no default constructor\n  This could cause a lot of log spam when list were being used\n  with partially applied operators.\n  Closes #721\n* Fix crash on operator pipes with invalid second operators\n  Closes CyclopsMC/IntegratedTunnels#160\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nChanges:\n* Make mod compat operator names more consistent\n  Related to CyclopsMC/IntegratedDynamics#591\n* Improve text alignment inside Logic Programmer, Closes #591\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.14.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nFixes:\n* Properly pass network instance to ticking parts\n  This may resolve problems with networks being corrupted in parts.\n  Related to #720\n* Fix part settings gui showing side field dropdown behind other inputs\n  Closes CyclopsMC/IntegratedTunnels#164\n* Resolve Z-fighting on writers that are placed next to each other\n  Closes #105\n* Fix networks never working when energy consumption is enabled\n  This was caused by the energy network never getting indexed.\n  Closes #731\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.15.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nAdditions:\n* Expose slotted ingredient networks\n  This significantly improves performance when external handlers are\n  interacting with networks, such as hoppers inserting or extracting\n  items.\n  Closes CyclopsMC/IntegratedTunnels#167\n\nFixes:\n* Fix ingredients duping when externals are interacting with IT interfaces\n  Fixes dupe bug with Thermal Expansion Item Allocator\n  Closes CyclopsMC/IntegratedTunnels#169\n* Fix counting in infinite lists causing infinite loop and crashing, Closes #748\n* Fix LP list creation incorrectly remembering values after deselecting, Closes #739\n* Improve Named and Uniquely Named value type categories, Closes #741, Closes #742\n* Fix mechanical machines not working if internal energy buffer too small\n  Even if there is a valid energy network available., Closes #743\n* Set default ingredientNetworkObserverFrequencyForced to 0\n  This fixes a problem where IT networks would not get re-indexed\n  by AE2 storage terminals.\n  This requires a config reset!\n  Related to CyclopsMC/IntegratedTunnels#166\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.16.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nFixes:\n* Fix performance issue when importing/exporting in IT\n  Closes CyclopsMC/IntegratedTunnels#170\n* Add type check when using combined operators with operators, Closes #749\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.17.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.1 or higher.\n\nFixes:\n* Fix oredict recipe item input quantities being lost, Closes #752\n* Fix crash when applying more variables than an operator can accept\n  This can be required in complex currying situations\n  where variables that don't fit into the inputs of one operator\n  should be delegated to the resulting operator.\n  Closes #754\n* Gracefully throw an evaluation error when building infinite operators, Closes #755\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.18.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.5.0 or higher.\n\nAdditions:\n* Link to infobook Web version\n  https://integrateddynamics.rubensworks.net/book/\n* Add registry exporters\n\nChanges:\n* Add keybinding infobook appendix localization\n* Properly implement getTranslationKey on ItemPart\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nChanges:\n* Use more oredict variants in recipes instead of fixed items, Closes #658\n* Optimize list equality checks by first checking length\n\nFixes:\n* Fix TOO LONG deserialization errors causing logging spam, Closes #664\n* Fix crash when oredict recipe slot is empty\n* Fix crash when making lists of complex subtypes\n* Fix cast exceptions inside operators not always being caught, Closes #644\n* Fix static and dynamic lists never being equal, Closes #640\n* Fix rare client crash when invalid labels are received, Closes #645\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nChanges:\n* Make strings part of the 'to string' value type, #650\n* Add more safety checks for invalid value casting\n\nFixes:\n* Fix list in logic programmer showing overlapping tooltips on output slot\n* Don't crash but fix if extraction finalization detected inconsistency, Closes #679\n* Ensure currying is done in operator-related operators, #650\n* Fix map operator not supporting currying, Closes #650\n* Fix crash when ingredient network positions are not loaded, #680\n* Fix pipe operator handling multi-parameters incorrectly, Closes #585\n* Fix list equality not considering value type categories\n* Fix stack overflow error when a class cast error occurs during list.get, Closes #649\n* Fix crash when creating lists of recipes, Closes CyclopsMC/IntegratedCrafting#4\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nAdditions:\n* Allow copying variables to multiple cards\n  This also fixes a duplication exploit when having multiple empty cards.\n\nFixes:\n* Fix getChannelIndex illegally returning null in some cases\n* Fix \"recipe by input\" operator incorrectly having a list output type\n* Properly handle chunk (un)loading in variable caches, CyclopsMC/IntegratedTunnels#148, Closes #683\n* Fix crash when not all positions are loaded in getMaxQuantity, Closes CyclopsMC/IntegratedTunnels#148\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nFixes:\n* Allow NBT values to be converted to string, Closes #697\n* Fix viewing corrupted variables causing a crash, Closes #691\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nFixes:\n* Fix mod version being hardcoded to 1.0.0\n\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nChanges:\n* Add more convenience (mechanical) squeezer minetweaker variants\n  CyclopsMC/IntegratedDynamics#700\n* Switch from trove to fastutil collections.\n  This slightly improves overall performance.\n\nFixes:\n* Fix z-fighting on variable cards, Closes #353\n* Fix one-off selection when clicking on operator dropdown in LP, Closes #701\n* Fix NBT representations being possible for a basic energy battery\n  Resolves autocrafting issue.\n  Closes CyclopsMC/IntegratedCrafting#17\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.0.0 or higher.\n\nChanges:\n* Dump stack on state corruption\n\nFixes:\n* Fix some optional-based CT handlers not working with missing args\n  Apparently CT does not work well with too many optionals.\n  This commit reduces the number of optionals to make this work again.\n  Closes CyclopsMC/IntegratedDynamics-Compat#21\n* Fix importing of item amounts >64 voiding everything >64\n  Closes CyclopsMC/IntegratedTunnels#158\n* Fix positioned parts being removed even if re-added in the same tick\n  This fixes an issue in IT where ingredient transfer would\n  stop because the index would become empty after a chunk reload.\n  Closes CyclopsMC/IntegratedTunnels#157\n* Partially fix writers not being updated with variable values\n  CyclopsMC/IntegratedTunnels#157\n* Save recipes in new CommonCapabilities format\n  This fixes issues where some oredict-based recipes\n  would become too big to store.\n  Closes #694\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.0.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.1.0 or higher.\n\nChanges:\n* Update to Forge 14.23.5.2768 and MCP snapshot_20180814\n* Use cheaper method for checking if a config is enabled, CyclopsMC/CyclopsCore#116\n\nFixes:\n* Fix variable store causing chunks to be always loaded, Closes #705\n* Fix wilcard chan interaction breaking after interface channel is changed\n* Fix storage interactions not causing an immediate sync, Closes CyclopsMC/IntegratedTerminals#25\n\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.5.0 or higher.\n\nAdditions:\n* Add string parse operators for basic value types, Closes #690\n* Add itemstack has NBT operator, Closes #693\n* Add string join operator, Closes #607\n* Add light level aspect to block reader\n* Add biome aspect to block reader\n* Add text to speech aspect to audio writer\n\nFixes:\n* Fix APPLY_2 and APPLY_3 producing wrong currying, Closes #767\n* Fix smart pressure plate challenge fulfillment conditions being wrong, Closes #770\n* Fix rare concurrency-related crash in ingredient observer, Closes #769\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.0 or higher.\n\nChanges:\n* Optimize compareTo in PartNetworkElement\n  It used to result in quite a bit of overhead during network ticking.\n\nFixes:\n* Fix crash when elements are removed before network is initialized\n  This could occur rarely on server lag.\n  Closes #784\n* Fix join operator not checking value types strictly enough, Closes #783\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.5 or higher.\n\nFixes:\n* Fix crash when reading from non-existing inventory, Closes #908\n* Fix apply producing wrong conditional output type for curried operators, Closes #897\n* Fix bucket being voided when filling a non-empty Drying Basin, Closes #903\n* Fix substring operator not checking length correctly, Closes #905\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.5 or higher.\n\nChanges:\n* Remove priority from PartNetworkElement hashCode\n  Improves performance #940\n\nFixes:\n* Fix crash when reading entity hurt/death sound, Closes #946\n* Fix lighting and orientation of facades Closes #180\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.0 or higher.\n\nChanges:\n* Improve performance of hasNext in list iterators\n  This used to cause performance issues with nested list concats.\n  Closes #800\n* Don't allow string joining on infinite lists\n\nFixes:\n* Use item equality method from CommonCapabilities\n  This fixes issues where NBT tags are configured to be ignored\n  in CommonCaps, but this is not propagated to item comparisons in ID.\n  Closes #795\n* Make NBT representation of battery energy contents unique\n  Integrated Crafting would distinguish between identical batteries\n  obtained through different methods due to unequal NBT,\n  which is fixed by this commit.\n  Closes CyclopsMC/CommonCapabilitiesAPI#8\n* Fix the network diagnostic tool's box rendering\n  - Expand box evenly, not just towards positive x/y/z\n  - Render full block bounding boxes correctly\n* Fix some problems with the network diagnostic tool's click handler\n  - Fix teleporting to full blocks\n  - Prevent additional NullPointerExceptions in click handler. Those occur if you click a row that belongs to another dimension.\n  - Refresh table model every time something is (un)marked. This gives the user more immediate visual feedback for their actions.\n* Fix sorting and selection in the network diagnostic tool\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.0 or higher.\n\nFixes:\n* Fix stackoverflow when calculating slots in self-referencing networks, Closes CyclopsMC/IntegratedTunnels#177\n* Fix some type checks being too strict for ANY values, Related to CyclopsMC/IntegratedTunnels#181\n* Fix monodirectionals breaking after removing with wrench and replacing, Closes #820\n* Make value casting within operators more type-safe, Closes #830\n  The old casting method never actually worked properly,\n  which caused problems with complex invalid operator values.\n* Fix incorrect white tulip recipe for mechanical squeezer, Closes #827\n* Fix batteries losing energy between saves, Closes #823\n* Fix incorrect position for replaceable check during cable placement, Closes #818\n  This resulted in a crash when placing cables on snow.\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.0 or higher.\n\nFixes:\n* Fix compilation issue do to recent ID change, Closes CyclopsMC/IntegratedDynamics#835\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.0 or higher.\n\nAdditions:\n* Allow blank recipe creation in LP, Closes #837\n\nChanges:\n* Increase LP text input fields to max 512 characters, Closes #841\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.0 or higher.\n\nFixes:\n* Fix ValueTypeListProxyEntityItems to use ItemStack.EMPTY instead of null\n* Replace null with ItemStack.EMPTY to avoid crash\n* Remove incorrect cyclic reference check\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.5 or higher.\n\nAdditions:\n* Add config options for power usage of individual parts, Closes #856\n\nChanges:\n* Add more debug information for invalid heterogeneous list proxy serialization, Related to #874\n* Don't persist NBT on pick block for variable-holding blocks\n  This could too easily duplicate variables,\n  and lead to problematic duplicate ids.\n  Closes #879\n\nFixes:\n* Fix battery combination energy only saving 2000 FE, Closes #892\n* Don't render cables when facaded\n  Improves FPS performance slightly.\n  Closes #853\n* Mutex ingredient positions based on thread id and positions\n  This fixes rare network recursion conflicts during ingredient movement.\n  Closes CyclopsMC/IntegratedTunnels#194\n* Don't print unneeded ingredient observation errors, Closes #858\n* Fix ingredient observer not working on plain reading\n  This fixes the problem of network-level energy readings never changing.\n* Fix crash when using mapped operator, Closes #866\n* Fix containers not opening from 8+ blocks, Closes CyclopsMC/IntegratedDynamics#842\n* Add null check in JEI transfer handler\n  This fixes a crash when showing all JEI recipes.\n  Closes #861\n* Fix recursion fail on proxy network value on self, Closes #845\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.5 or higher.\n\nFixes:\n* Fix set slot packet exploit\n\n"
  },
  {
    "path": "resources/changelog/1.12.2-1.1.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.6.5 or higher.\n\nFixes:\n* Fix set slot packet exploit not being fully fixed yet\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.1 or higher.\n\nChanges:\n* Add overworld biome type to meneglin biome, Closes #885\n\nFixes:\n* Fix torches not being placeable on walls\n* Update to latest RS version to fix class not found crash, Closes #889\n* Fix Menril Torches missing transparency, Closes #886\n* Ensure all sides of menril tree are on solid ground, Closes #883\n* Fix Menril Trees spawning on water and trees, Closes #883\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.1 or higher.\n\nChanges:\n* Rebalance drops and spawn rate of Menril Trees, Closes #888\n\nFixes:\n* Fix incorrect Input Variable Transformer recipe, Closes #887\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.1 or higher.\n\nChanges:\n* Don't render cables when facaded\n  Improves FPS performance slightly.\n  Closes #853\n* Add more debug information for invalid heterogeneous list proxy serialization, Related to #874\n\nFixes:\n* Mutex ingredient positions based on thread id and positions\n  This fixes rare network recursion conflicts during ingredient movement.\n  Closes CyclopsMC/IntegratedTunnels#194\n* Don't print unneeded ingredient observation errors, Closes #858\n* Fix ingredient observer not working on plain reading\n  This fixes the problem of network-level energy readings never changing.\n* Fix crash when using mapped operator, Closes #866\n* Fix containers not opening from 8+ blocks, Closes CyclopsMC/IntegratedDynamics#842\n* Add null check in JEI transfer handler\n  This fixes a crash when showing all JEI recipes.\n  Closes #861\n* Don't persist NBT on pick block for variable-holding blocks\n  This could too easily duplicate variables,\n  and lead to problematic duplicate ids.\n  Closes #879\n* Fix recursion fail on proxy network value on self, Closes #845\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.0 or higher.\n\nFixes:\n* Remove conflicting cactus recipe, Closes #893\n* Fix energy network always being empty\n  This caused problems with energy-consuming networks.\n  Closes #895\n* Fix incorrect and missing flower squeezing recipes, Closes #893\n* Fix display panel crash when errors field is null, Closes #899\n* Fix crash when creating object-based list, Closes #896\n* Fix console error when shift-clicking into labeler\n* Fix labeler not catching all keypresses, Closes #891\n* Add config option to disable meneglin biome in overworld\n  This replaces the broken meneglinBiomeDimensionBlacklist option.\n  Closes #890\n* Fix variable store dropping variables twice\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.14.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.1 or higher.\n\nFixes:\n* Fix part models not updating upon variable change\n* Fix incorrect unknown operator error in infobook loader\n* Fix invalid getSignature method in ValueTypeOperator\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.15.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.2 or higher.\n\nFixes:\n* Fix logic programmer hotkeys not working with modifiers\n* Fix issues related to Integrated Terminals\n* Fix ingredient capabilities not being registered\n\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.16.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.3 or higher.\n\nFixes:\n* Fix invalid value interface values\n  Required for Integrated REST\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.17.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.3 or higher.\n\nFixes:\n* Fix server crash while producing invalid value type errors, Closes #911\n* Fix crash when reading from non-existing inventory, Closes #908\n* Fix apply producing wrong conditional output type for curried operators, Closes #897\n* Fix bucket being voided when filling a non-empty Drying Basin, Closes #903\n* Fix substring operator not checking length correctly, Closes #905\n* Fix set slot packet exploit\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.18.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.5 or higher.\n\nAdditions:\n\nChanges:\n\nFixes:\n* Fix JEI crash on importing fluid-related recipes\n  Closes CyclopsMC/IntegratedDynamics#914\n* Use unsafe tile entity getter in IngredientObserver threads to fix TPS issues., Closes #917\n* Fix Squeezer causing X-Ray behaviour, Closes #920\n* Fix Menril Door dropping two items, Closes #919\n* Fix coal generator duplicating drops, Closes #924\n* Fix server crash when opening block reader, Closes #923\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.19.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.8.5 or higher.\n\nFixes:\n* Fix client crash when opening recipe tab on LP, Closes #928\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.20.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.9.0 or higher.\n\nFixes:\n* Don't block main thread when a previous observer was still running\n  An alternative fix for #917, as the previous fix caused race conditions (#931).\n* Fix invalid EvilCraft recipes\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.0 or higher.\n\nInitial 1.15 port\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.0 or higher.\n\nChanges:\n* Mark Common Capabilities as a required dependency, Closes #877\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.0 or higher.\n\nFixes:\n* Widen ID version range in compat\n  Fixes version ranges issues in ID.\n  Closes CyclopsMC/IntegratedDynamics#880\n\n"
  },
  {
    "path": "resources/changelog/1.15.2-1.1.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.7.0 or higher.\n\nFixes:\n* Tweak registration of IngredientComponentHandlers\n  A potential fix for #875\n* Fix meneglin biome features failing to register\n  This could cause crashes during startup.\n  Closes #878\n  May be related to #875\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.1.20.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.9.0 or higher.\n\nInitial 1.16 release\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.1.21.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.9.0 or higher.\n\nAdditions:\n\nChanges:\n\nFixes:\n* Add more biome dict types for Meneglin\n  Fixes biome not spawning in Biomes O Plenty\n* Fix squeezer rotation when neighbours are updated, Closes #949\n* Fix advancement with invalid rewards\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.1.22.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.9.0 or higher.\n\nAdditions:\n* Add more squeezer ore processing recipes\n  Added: aluminium, osmium, uranium, zinc\n\nFixes:\n* Fix part placement without a cable, Closes #952\n* Fix squeezer ore processing recipes using wrong tags\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.2.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.9.5 or higher.\n\nAdditions:\n* Improve cable facade rendering\n* Show recipe duration and energy usage in JEI, Closes #958\n* Show variable recipe output items when holding shift\n\nChanges:\n* Update zh-CN translation\n* Remove priority from PartNetworkElement hashCode, Improves performance #940\n\nFixes:\n* Fix console spawn when reading a machine's recipes\n* Fix meneglin biome still spawning when disabling it\n  This also removes the generateInOverworld option,\n  which can now be achieved by setting spawnWeight to 0.\n  Closes #951\n* Fix NBT.as_string operator having wrong output type, Closes #941\n* Fix proxy and materializer being places with wrong facing\n* Fix drying basin sometimes duplicating fluids, Closes #954\n* Fix incorrect facade item rendering, Related to #961\n* Fix crash when reading entity hurt/death sound, Closes #946\n* Fix lighting and orientation of facades Closes #180\n* Fix incorrect aluminium recipes\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.3.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.10.0 or higher.\n\nAdditions:\n* Improve selection of tags for defining recipes in LP\n\nFixes:\n* Fix broken recipes when leaving first or second output empty, Closes CyclopsMC/IntegratedCrafting#52\n* Fix zh-CN translation issues\n* Fix tool_for_obsidian advancement not being obtainable, Closes #969\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.4.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.0 or higher.\n\nAdditions:\n* Enable tag-based recipe output prioritization for machines\n  This is done with the new `recipeTagOutputModPriorities` config option.\n  Closes #976\n\nFixes:\n* Fix incorrect Red Dye recipes in (Mechanical) Squeezer, Closes #977\n* Fix battery auto-supply crash, Closes #970\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.4.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.0 or higher.\n\nChanges:\n* Enable Forge version checker\n* [ImgBot] Optimize images (#978)\n\nFixes:\n* Fix serialization issues with configured features, Closes #975\n* Fix crash when throwing EvaluationException server-side, Closes #981\n* Fix crash when determining mod name of an undefined block or entity, Closes #982\n* Fix incorrect rendering of transparent facades, Closes #980\n* Fix server crash when restarting after removing a dimension, Closes CyclopsMC/CyclopsCore#149\n* Fix orientation of top and bottom facade faces, Closes #974\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.4.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.0 or higher.\n\nFixes:\n* Fix crash when facade model has no quads, Closes #987\n* Fix crash when Menril Sapling grows, Closes #985\n* Add temporary workaround to fix LazyOptional race condition\n  This seems to be caused by a race condition in Forge,\n  for which a fix is already pending.\n  We can remove this workaround once the fix is merged.\n  https://github.com/MinecraftForge/MinecraftForge/issues/7610\n  Closes #986\n"
  },
  {
    "path": "resources/changelog/1.16.4-1.4.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.0 or higher.\n\nFixes:\n* Fix server crash on JEI autocompletion for recipes\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nAdditions:\n* Allow boolean values to be set via checkbox in LP and aspect settings\n  Closes CyclopsMC/IntegratedTunnels#186\n\nChanges:\n* Ensure observer world reads happen in main world thread\n  This fixes many weird race conditions and mod conflicts\n  caused by reading the world within another thread in an\n  unsafe manner. Closes #1081\n* Drop variables when breaking/wrenching parts in creative mode, Closes #1084\n\nFixes:\n* Fix priorities not being considered in ingredient networks\n  This could for example cause Integrated Tunnels networks\n  ignoring priorities when exporting items.\n  Closes CyclopsMC/IntegratedTunnels#244\n* Fix colored facades (like grass) missing colors, Closes #1092\n* Fix cable-less parts connecting to network in recent Forge versions\n  Closes CyclopsMC/IntegratedTunnels#243\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix server crash due to loading client logic in cable config, Closes #1093\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nChanges:\n* Make cables with facades acts as proper full blocks\n  This fixes problems such as torches and levers\n  not being placeable on cables with facades,\n  and walls not connecting.\n  Closes #1159\n\nFixes:\n* Fix dupe bug with item exporter\n  This was caused by ingredient indexes being able to produce\n  duplicate matches when requesting positions for ingredients.\n  Closes CyclopsMC/IntegratedTunnels#255\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix rare rendering crash caused by mods such as Optifine, Closes #1186\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix crash when nesting regex groups, Closes #1190\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix audio writer not supporting sounds from custom datapacks, Closes #1191\n* Fix omni-directional connectors not always receiving id's on craft\n  This rewrites omni-directional connector crafting using custom recipe serializers.\n  Closes #1189\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.14.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix NBT reading of empty itemstacks\n  This fixes read NBT data not updating after non-player item move.\n  When things like hoppers drain items from inventories,\n  the stack size is set to zero, but the NBT tag is still\n  present on that item.\n  Because of that, we should only allow NBT reading of non-empty items\n  Closes #1208\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.15.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix checkbox render issues in recipe gui in LP\n* Fix crash on invalid characters in recipe tags, Closes #1209\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix cables having black particles, Closes #1097\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nChanges:\n* Allow draining/filling batteries in item-form when stacked\n  This also fixes power dupe issues with mods such as Mekanism\n  Closes #1100\n\nFixes:\n* Cache expensive positioned storage lookup for a tick\n  This improves performance for massive networks with passive interaction.\n  Closes #1101\n* Fix crash when rendering with Chisel, Closes #1098\n* Fix notes in audio writer not working, Closes #1099\n* Fix type in recipe handling tutorial\n\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix filled variant not included in menril_logs tag, Closes #1106\n* Fix Drying Basin not handling recipes without output items, Closes #1105\n* Fix amount formatting errors in other locales\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nChanges:\n* Allow shearing of leaves with modded shears, Closes #1107\n\nFixes:\n* Fix JEI recipes not being deeply copied into LP\n  Closes CyclopsMC/IntegratedDynamics#1109\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix energy dupe bug when crafting batteries together, Closes #1113\n  Related to #1100\n* Fix BlockCable$BlockColor#getColor not checking null args\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nChanges:\n* Automatically recreate corrupted networks, Closes #1121\n  This could occur on chunk or server data corruptions,\n  and will now be restored on first world tick.\n  This can be disabled with `recreateCorruptedNetworks`.\n\nFixes:\n* Fix applied vars in curried operators not being materialized, Closes #1123\n* Fix small delay on ingredient detection when inserting into network\n  This was observable in Integrated Terminals where the inserted\n  ingredient would not immediately show up in the storage.\n* Fix crash when filling/draining empty battery items, Closes #1116\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Retry reads from external networks externally on error, Closes #1145\n* Fix crash when other mods call the facade recipe too early, Closes #1143\n* Fix text to speech being too frequent and not stopping, Closes #1138\n* Fix opaque facades not blocking facades, Closes #1137\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.10.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nAdditions:\n* Add config option to disable dynamic cable shapes\n\nChanges:\n* Emit ingredient change events synchronously on sync observations\n  Required for CyclopsMC/IntegratedCrafting#81\n\nFixes:\n* Cache cable collision shapes to improve client performance, Closes #1155\n* Fix first recipe output slots in JEI not showing chances\n  Closes CyclopsMC/IntegratedDynamics#1146\n* Fix battery capacity being too high after placing from stack, Closes #1150\n* Allow particle offsets to be negative, Closes #1148\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.5.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nAdditions:\n* Add Menril Fence and Menril Fence Gate, #272\n* Add Crystalized Chorus and Menril Slabs, #272\n* Add Menril Slabs, #272\n* Add composter support for menril leaves, sapling and berries\n* Add Menril Wood and stripped variants, Closes #993\n\nFixes:\n* Fix typo in infobook, Closes #994\n* Resolve minor infobook issues\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.5.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nFixes:\n* Fix incorrectly formatted error messages in parts, #996\n* Fix crash when selecting Recipe in List LP element, Closes #1001\n* Fix random NPE when opening GUIs, Closes #999\n* Fix crash when throwing EvaluationException with value types server-side, Closes #998\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.6.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nAdditions:\n* Expose check for pending forced ingredient network observation\n  Required for CyclopsMC/IntegratedCrafting#48\n\nFixes:\n* Fix wrong entity being read in entity reader, Closes #1003\n  This caused entities to sometimes contain wrong data.\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.6.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nFixes:\n* Fix crash when cables are removed indirectly, Closes #1005\n* Fix Materializer not being able to handle curried operators, Closes #1004\n* Fix wrong text rendering in delay gui\n* Fix Materializer and Proxy not immediately noticing slot changes, #1004\n* Fix crash when inserting invalid sound name in audio writer, Closes #1006\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.7.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nAdditions:\n* Add Fluid.with_amount operator, Closes #846\n\nChanges:\n* Make plant type operator return values more consistent when empty\n\nFixes:\n* Use `forge:tools/wrench` as Wrench tag\n  This aligns better with other mods.\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.7.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nFixes:\n* Fix crash while deserializing curried operators, Closes #1007\n* Fix IngredientObserver not allowing servers to be stopped, Closes #1013\n* Fix rare crash on cable shape lookup, Closes #1014\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.7.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nFixes:\n* Fix crash when showing all recipes in JEI, Closes #1018\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.8.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.1 or higher.\n\nAdditions:\n* (core change, required for Integrated Tunnels)\n  Allow filters to be placed on positions in ingredient networks\n  Required for CyclopsMC/IntegratedTunnels#98\n\nChanges:\n* Migrate energy ingredient component to long-based values\n  Required for #1023\n\nFixes:\n* Fix going in and out of safe-mode corrupting all networks, Mentioned in #1034\n* Fix erroring aspects not updating anymore, Closes #1035\n  This was the cause of network reader aspects always showing\n  ERROR, even after a cable was added in front of it to enable\n  these aspects.\n* Fix itemstack values always materializing to stacksize 1, Closes #1025\n* Fix item list count operator crashing on heterogeneous lists, Closes #1030\n* Fix crash when creating list of operators, Closes #1031\n* Fix crash when starting a singleplayer world for the second time\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.8.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nChanges:\n* Allow observers to be invoked manually synchronously\n  Required for CyclopsMC/IntegratedCrafting#56\n\nFixes:\n* Fix block state changes not always triggering aspect variable updates, Closes #1029\n* Fix crash when using energy-based recipes, Closes CyclopsMC/IntegratedCrafting#60\n* Fix cables sometimes having wrong collision boxes, Closes #862\n* Fix facade items for blocks such as stone to flicker each tick\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.8.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix Fluid with amount operator having wrong output type, Closes #1044\n* Fix crash when cable does not have collision box, Closes #1043\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.8.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\ns\nFixes:\n* Fix recipe slot count modification not being applied in LP\n  Closes CyclopsMC/IntegratedCrafting#63\n* Fix labels not being sent to clients after corruption\n  Closes #1038\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.8.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix rare deadlock when using Crafting Writer\n  Closes CyclopsMC/IntegratedCrafting#62\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.8.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nChanges:\n* Allow removal of aspect properties\n  Required for CyclopsMC/IntegratedTunnels#231\n\nFixes:\n* Add sanity checks in set slot packet, Closes CyclopsMC/IntegratedDynamics-Compat#27\n* Fix fluid rendering on cables with facades, Closes #1055\n* Fix strong redstone power option not always causing updates, Closes #1053\n* Fix some operators using client-side logic in error reporting, Closes #1054\n* Fix Display Panels not rotating correctly, Closes #1051\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.9.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nAdditions:\n* Add operators for interacting with block properties\n  These properties correspond to blockstates.\n  Closes #765\n\nFixes:\n* Fix missing bone to dye recipe in Mechanical Squeezer, Closes #1060\n\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.9.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix networks corrupting after restart on OpenJ9 JVM\n  Caused by an odd FML edge-case.\n  Closes CyclopsMC/IntegratedTunnels#212\n* Fix focus issues after labelling within logic programmer, Closes #1061\n* Fix temporary client desync when copying omni-dir connectors, Closes #1062\n* Fix sneak actions not working while crawling, Closes #1064\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.9.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nAdditions:\n* Expose index of IngredientChannelIndexed\n  Required for Integrated Crafting.\n\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.9.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix blank named cards not inheriting label after being assigned an id, Closes #1074\n* Fix not all keys working in LP labeller, Closes #1073\n"
  },
  {
    "path": "resources/changelog/1.16.5-1.9.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.11.6 or higher.\n\nFixes:\n* Fix console error on displaying ore recipes in JEI\n  Closes CyclopsMC/IntegratedDynamics#1082\n* Fix full network blocks not disconnecting on placing parts inbetween\n  This caused issues when for example placing energy interfaces against\n  batteries that were already connected directly.\n  Closes #1077\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix crash when using block reader since Forge 39.0.45, Closes #1133\n\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix missing translation entries for some particle effect aspects\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Retry reads from external networks externally on error, Closes #1145\n* Fix crash when other mods call the facade recipe too early, Closes #1143\n* Fix text to speech being too frequent and not stopping, Closes #1138\n\n\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nAdditions:\n* Add config option to disable dynamic cable shapes\n\nChanges:\n\nFixes:\n* Fix opaque facades not blocking facades, Closes #1137\n* Cache cable collision shapes to improve client performance, Closes #1155\n* Emit ingredient change events synchronously on sync observations\n  Required for CyclopsMC/IntegratedCrafting#81\n* Fix first recipe output slots in JEI not showing chances\n  Closes CyclopsMC/IntegratedDynamics#1146\n* Fix battery capacity being too high after placing from stack, Closes #1150\n* Allow particle offsets to be negative, Closes #1148\n\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nInitial 1.18 release\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix advancements triggering too eagerly\n  Closes CyclopsMC/IntegratedDynamics#1125\n\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nFixes:\n* Fix crafting advancements triggering too eagerly\n  Closes CyclopsMC/IntegratedDynamics#1125\n\n"
  },
  {
    "path": "resources/changelog/1.18.1-1.10.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.0 or higher.\n\nChanges:\n* Automatically recreate corrupted networks, Closes #1121\n  This could occur on chunk or server data corruptions,\n  and will now be restored on first world tick.\n  This can be disabled with `recreateCorruptedNetworks`.\n\nFixes:\n* Fix applied vars in curried operators not being materialized, Closes #1123\n* Fix small delay on ingredient detection when inserting into network\n  This was observable in Integrated Terminals where the inserted\n  ingredient would not immediately show up in the storage.\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.10.13.txt",
    "content": "As always, don't forget to backup your world before updating!\n\nInitial 1.18.2 release.\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.10.14.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.5 or higher.\n\nFixes:\n* Fix tag-based recipe outputs producing invalid results, Closes #1158\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.10.15.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.5 or higher.\n\nFixes:\n* Fix invalid packet serialization of items from ingredients\n  This fixes errors when connecting to servers.\n  Closes CyclopsMC/IntegratedDynamics#1161\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.10.16.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.5 or higher.\n\nChanges:\n* Make cables with facades acts as proper full blocks\n  This fixes problems such as torches and levers\n  not being placeable on cables with facades,\n  and walls not connecting.\n  Closes #1159\n\nFixes:\n* Fix dupe bug with item exporter\n  This was caused by ingredient indexes being able to produce\n  duplicate matches when requesting positions for ingredients.\n  Closes CyclopsMC/IntegratedTunnels#255\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.10.17.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.5 or higher.\n\nFixes:\n* Fix crash when creating recipe values with empty tags, Closes #1168\n* Fix squeezer breaking recipes after usage, Closes #1166\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.10.18.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.13.5 or higher.\n\nAdditions:\n* Add Korean Localization\n\nChanges:\n* Improve performance of network ticks\n\nFixes:\n* Fix crash when creating recipe values with empty tags, Closes #1168\n* Fix squeezer breaking recipes after usage, Closes #1166\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.14.0 or higher.\n\nChanges:\n* Rebalance ore doubling recipes in (mechanical) squeezer, Closes #1129\n\nFixes:\n* Fix energy battery overflows in toolips when stacked, Closes #1176\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.15.0 or higher.\n\nFixes:\n* Update to refactored inventory iteration in CyclopsCore\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Backport JEI changes from MC 1.19.\n  This makes the mod compatible with both JEI 9 and JEI 10\n  Closes CyclopsMC/IntegratedDynamics#1210\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix crash on invalid characters in recipe tags, Closes #1209\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix omni-dir connector recipe not listed in infobook\n* Fix machine reader crash on drying basin, Closes #1212\n* Fix writer-based advancements for items triggering too soon, Closes CyclopsMC/IntegratedTunnels#258\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nChanges:\n* Cache slot counts of ingredient networks for a tick\n  This improves performance significantly for very large networks when they are being iterated over.\n  Closes #1229\n* Clarify meaning of consumption rate aspect, Closes #1216\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.15.0 or higher.\n\nFixes:\n* Align squeezer item dropping logic with vanilla chests\n  This may fix duping problems with mods such as CarryOn.\n  Closes #1178\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.15.0 or higher.\n\nFixes:\n* Fix Mechanical Drying Basin only producing recipe outputs once, Closes #1181\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.15.0 or higher.\n\nFixes:\n* Fix crash when creating recipe with empty inputs, Closes #1182\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.15.0 or higher.\n\nFixes:\n* Fix rare crash due to rendering null facade, Closes #1183\n* Fix checkbox render issues in recipe gui in LP\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.15.0 or higher.\n\nFixes:\n* Fix crash when nesting regex groups, Closes #1190"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix audio writer not supporting sounds from custom datapacks, Closes #1191\n* Fix omni-directional connectors not always receiving id's on craft\n  This rewrites omni-directional connector crafting using custom recipe serializers.\n  Closes #1189\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Update to JEI 10.x, Closes CyclopsMC/IntegratedDynamics#1201\n* Fix machine gui crash upon client desync on fluid data, Closes #1197\n* Fix squeezers unable to handle deepslate coal ore, Closes #1199\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.11.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nChanges:\n* Rename 'Crystalized' to 'Crystallized', Closes #1206\n\nFixes:\n* Fix NBT reading of empty itemstacks\n  This fixes read NBT data not updating after non-player item move.\n  When things like hoppers drain items from inventories,\n  the stack size is set to zero, but the NBT tag is still\n  present on that item.\n  Because of that, we should only allow NBT reading of non-empty items\n  Closes #1208\n\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.12.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nAdditions:\n* Add Network Diagnostics tool for server operators, Closes #863\n  This restores the pre-1.15 diagnostics, but uses a Web browser to display it instead of the deprecated AWT libraries.\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.12.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nChanges:\n* Output 3 items when copying Omni-directional Connector, Closes #1238\n\nFixes:\n* Fix some blocks being slow to break, Closes #1232\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.12.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix corruption of mineable/pickaxe tag, Closes #1240\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.12.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix backspace not updating label in Logic Programmer, Closes #1239\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.12.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Make invalid part containers not crash\n  This makes the game not crash anymore when frame-like mods are used.\n  Closes #1198\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.12.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix JEI recipe modification after modifying in LP\n  Closes CyclopsMC/IntegratedCrafting#89\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nAdditions:\n* Allow recipe ingredients to be marked as reusable\n  Required for CyclopsMC/IntegratedCrafting#36\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix crash on filling recipes with JEI\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nChanges:\n* Use collapsed ingredient storage by default\n  Ingredient networks will now perform better for match-based lookups.\n  Related to CyclopsMC/IntegratedDynamics#1247\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix delayer not propagating errors, Closes #1251\n* Fix delay error message not being shown in gui, Related to #1251\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix double-shadow text printing in LP\n* Fix missing text colors in operator LP display\n* Fix Logic Programmer interface for lists of complex types\n  This makes it possible to makes lists of recipes, ingredients,\n  and operators.\n  Closes #641\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix broken Dynamic Light Panels, Closes #1276\n* Fix mechanical recipes incorrectly being validated\n  This fixes Integrated Crafting crafters not working on them.\n  Closes CyclopsMC/IntegratedDynamics#1275\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix inability to remove text from recipes in LP, Closes #1280\n* Fix formatting error in operator cards tooltips, Closes #1281\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nChanges:\n* Give DOUBLE higher precedence than LONG for number operations, Closes #1284\n\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.13.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix invisible light crash in debug worlds, Closes #1287\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.14.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Add generic variants of increment, decrement, and modulus\n  These were only usable for integers, but can now be used for all numbers.\n\nFixes:\n* Fix wrong error message for invalid applied operators, Closes #1293\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.15.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Expose getChannelSlotted in positioned addons network\n  Required for CyclopsMC/IntegratedTunnels#271\n\nFixes:\n* Fix rare crash when getting priority of moved network parts, Closes #1299\n* Fix crash when getting name of variable with ANY type, Closes #1301\n* Fix backspace not working in delayer GUI, Closes #1298\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.16.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Expose positions from channels\n  This is required for CyclopsMC/IntegratedCrafting#98\n\nFixes:\n* Fix predicate-based tunnels movement ignoring channels, Closes CyclopsMC/IntegratedTunnels#274\n* Fix crash on special operator with ANY type, Closes #1301\n* Fix Menril Tree replacing grass with dirt on stump place fail, Closes #1304\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.16.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix held items vanishing after gui close, Closes #1308\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.17.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Add initialChange flag to storage change events\n  This is required to fix CyclopsMC/IntegratedCrafting#99\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.17.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix NBT.from_tag_list not working on Any lists, Closes #1315\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.17.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix predicate-based filtering interfaces causing ingredient loss\n  Integrated Tunnels filtering interaces could lose items\n  when using predicate-based filters.\n  Closes CyclopsMC/IntegratedTunnels#282\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.17.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix hard crash on replace_regex with invalid group, Closes #1317\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.17.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix tps and ticktime aspects returning values different to Forge, Closes #1325\n\n"
  },
  {
    "path": "resources/changelog/1.18.2-1.17.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix crash for illegal item tag chars in recipe in LP, Closes CyclopsMC/IntegratedCrafting#103\n* Fix Dank Storage item removal when placing cables, Closes #1332\n"
  },
  {
    "path": "resources/changelog/1.19-1.11.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nInitial 1.19 release.\n\n"
  },
  {
    "path": "resources/changelog/1.19-1.11.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.0 or higher.\n\nFixes:\n* Fix rare crash due to rendering null facade, Closes #1183\n* Fix checkbox render issues in recipe gui in LP\n* Fix crash when creating recipe with empty inputs, Closes #1182\n"
  },
  {
    "path": "resources/changelog/1.19-1.11.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.1 or higher.\n\nChanges:\n* Update to Forge 41.0.99\n"
  },
  {
    "path": "resources/changelog/1.19-1.11.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.16.1 or higher.\n\nFixes:\n* Fix crash when nesting regex groups, Closes #1190"
  },
  {
    "path": "resources/changelog/1.19-1.11.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix audio writer not supporting sounds from custom datapacks, Closes #1191\n* Fix omni-directional connectors not always receiving id's on craft\n  This rewrites omni-directional connector crafting using custom recipe serializers.\n  Closes #1189\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.11.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix crash on invalid characters in recipe tags, Closes #1209"
  },
  {
    "path": "resources/changelog/1.19.2-1.11.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix omni-dir connector recipe not listed in infobook\n* Fix machine reader crash on drying basin, Closes #1212\n* Fix writer-based advancements for items triggering too soon, Closes CyclopsMC/IntegratedTunnels#258\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.11.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nUpdate to MC 1.19.2\n\nFixes:\n* Fix machine gui crash upon client desync on fluid data, Closes #1197\n* Fix squeezers unable to handle deepslate coal ore, Closes #1199"
  },
  {
    "path": "resources/changelog/1.19.2-1.11.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Improve description of speachMaxFrequency property\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.11.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nChanges:\n* Rename 'Crystalized' to 'Crystallized', Closes #1206\n\nFixes:\n* Fix NBT reading of empty itemstacks\n  This fixes read NBT data not updating after non-player item move.\n  When things like hoppers drain items from inventories,\n  the stack size is set to zero, but the NBT tag is still\n  present on that item.\n  Because of that, we should only allow NBT reading of non-empty items\n  Closes #1208\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.12.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Add compact operator, for shortening numbers on displays, Closes #1217\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.13.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Add intersection operator\n* Add the forge:books tag to info book\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.13.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nChanges:\n* Cache slot counts of ingredient networks for a tick\n  This improves performance significantly for very large networks when they are being iterated over.\n  Closes #1229\n* Clarify meaning of consumption rate aspect, Closes #1216\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Add Network Diagnostics tool for server operators, Closes #863\n  This restores the pre-1.15 diagnostics, but uses a Web browser to display it instead of the deprecated AWT libraries.\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix crash when other mods shadow Netty\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nChanges:\n* Output 3 items when copying Omni-directional Connector, Closes #1238\n\nFixes:\n* Fix some blocks being slow to break, Closes #1232\n* Fix incorrect unlocalized fluid names, Closes CyclopsMC/EvilCraft#950\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix corruption of mineable/pickaxe tag, Closes #1240\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.4.txt",
    "content": "\n\nAs always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix corruption of mineable/pickaxe tag, Closes #1240\n\n\nThis is the same update as 1.14.3 to force CurseForge to update."
  },
  {
    "path": "resources/changelog/1.19.2-1.14.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix backspace not updating label in Logic Programmer, Closes #1239\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Make invalid part containers not crash\n  This makes the game not crash anymore when frame-like mods are used.\n  Closes #1198\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.14.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix JEI recipe modification after modifying in LP\n  Closes CyclopsMC/IntegratedCrafting#89\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.15.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nAdditions:\n* Allow recipe ingredients to be marked as reusable\n  Required for CyclopsMC/IntegratedCrafting#36\n\nFixes:\n* Fix JEI recipe modification after modifying in LP\n  Closes CyclopsMC/IntegratedCrafting#89\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.15.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.0 or higher.\n\nFixes:\n* Fix crash on filling recipes with JEI\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.15.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.0 or higher.\n\nChanges:\n* Use collapsed ingredient storage by default\n  Ingredient networks will now perform better for match-based lookups.\n  Related to CyclopsMC/IntegratedDynamics#1247\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add support for static and dynamic offsets\n* Render offsets when holding wrench\n* Add modes to wrench to set offset and side of parts\n* Balance offsets using enhancement items\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Enable offsets in Redstone Writers\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix invisible light crash in debug worlds, Closes #1287\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix delayer not propagating errors, Closes #1251\n* Fix delay error message not being shown in gui, Related to #1251\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix part enhancements dropping for offset 0, Closes #1259\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Set default part offset render distance to 16\n  This resolves performance issues due to too high render distances.\n  Existing worlds will have to manually change their configs for this change to apply (partOffsetRenderDistance).\n  Closes #1261\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix offset render delay when crouching\n  Closes CyclopsMC/IntegratedTunnels#267\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix double-shadow text printing in LP\n* Fix missing text colors in operator LP display\n* Fix Logic Programmer interface for lists of complex types\n  This makes it possible to makes lists of recipes, ingredients,\n  and operators.\n  Closes #641\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix broken Dynamic Light Panels, Closes #1276\n* Fix mechanical recipes incorrectly being validated\n  This fixes Integrated Crafting crafters not working on them.\n  Closes CyclopsMC/IntegratedDynamics#1275\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix part comparator depending on state\n  This could cause problems where chunks would be reloaded during unloading.\n  This issue was introduced when part offsets were added.\n  Closes #1257\n* Fix inability to remove text from recipes in LP, Closes #1280\n* Fix formatting error in operator cards tooltips, Closes #1281\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.16.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Give DOUBLE higher precedence than LONG for number operations, Closes #1284\n\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.17.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add generic variants of increment, decrement, and modulus\n  These were only usable for integers, but can now be used for all numbers.\n\nFixes:\n* Fix wrong error message for invalid applied operators, Closes #1293\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.18.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Expose getChannelSlotted in positioned addons network\n  Required for CyclopsMC/IntegratedTunnels#271\n\nFixes:\n* Fix rare crash when getting priority of moved network parts, Closes #1299\n* Fix crash when getting name of variable with ANY type, Closes #1301\n* Fix backspace not working in delayer GUI, Closes #1298\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.19.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Expose positions from channels\n  This is required for CyclopsMC/IntegratedCrafting#98\n\nFixes:\n* Fix predicate-based tunnels movement ignoring channels, Closes CyclopsMC/IntegratedTunnels#274\n* Fix crash on special operator with ANY type, Closes #1301\n* Fix Menril Tree replacing grass with dirt on stump place fail, Closes #1304\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.19.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix held items vanishing after gui close, Closes #1308\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.20.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add initialChange flag to storage change events\n  This is required to fix CyclopsMC/IntegratedCrafting#99\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.20.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix NBT.from_tag_list not working on Any lists, Closes #1315\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.20.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix predicate-based filtering interfaces causing ingredient loss\n  Integrated Tunnels filtering interaces could lose items\n  when using predicate-based filters.\n  Closes CyclopsMC/IntegratedTunnels#282\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.20.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix hard crash on replace_regex with invalid group, Closes #1317\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.21.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Expose interact names for operators. This is required for Integrated Scripting.\n* Add apply_0 operator\n* Add apply_n operator\n\nChanges:\n* Add INetwork parameter to variable facade methods\n\nFixes:\n* Fix duplicate variable invalidate listeners being registered\n  This could leak to memory leaks for long-running multi-arg operators.\n\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.21.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix part offsets sometimes being delayed by one aspect update, Closes #1320\n* Fix errored offset variables not resetting after network reset, Closes #1321\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.21.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix tps and ticktime aspects returning values different to Forge, Closes #1325\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.21.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Allow listening to EvaluationException resolutions\n  Required for CyclopsMC/IntegratedScripting#5\n\nFixes:\n* Fix crash for illegal item tag chars in recipe in LP, Closes CyclopsMC/IntegratedCrafting#103\n* Fix Dank Storage item removal when placing cables, Closes #1332\n* Fix facades being craftable for non-solid blocks, Closes #1334, #1342\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.22.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Allow checking if there are variable-based offsets in API\n  Required for CyclopsMC/IntegratedTunnels#289\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.22.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Also copy label when copying variable cards, Closes #1354\n* Allow Ingredient.with* operators to have empty ingredient inputs\n* Allow entering items in logic programmer with larger stack sizes\n\nFixes:\n* Fix default labeller textfield being modifiable, Closes #1352\n* Fix apply_0 crashing on operators with input, Closes CyclopsMC/IntegratedScripting#11\n* Fix incorrect comparator in PrioritizedPartPos\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.22.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Interrupt speech before sending a new one to the narrator, Closes #1356\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add Terrablender compat for Meneglin biome\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix op_by_name crashing if ResourceLocation is invalid, Closes #1381\n* Fix regex scan producing illegal lists for non-zero groups, Closes #1378\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix variables dependent on offset aspects not always updating, Closes #1416\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Drop Part Offsets into their original form, Closes #1418\n\nFixes:\n* Fix creative batteries not providing energy, Closes #1421\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Delay Terrablender registration\n  This fixes rare crashes when Terrablender was not yet fully initialized.\n  CyclopsMC/IntegratedDynamics#1385\n  Closes CyclopsMC/IntegratedDynamics#1388\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix JEI ghosts items not working for lists in the LP, Closes CyclopsMC/IntegratedDynamics#1398\n* Fix wrong sided part being shown in The One Probe, Closes #1401\n* Fix round-robin misbehaving with filtered interfaces, Closes CyclopsMC/IntegratedTunnels#302\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix incorrect type checking in complex reduce operation, Closes #1387\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Optimize ingredient positions index\n  This improves performance with Integrated Tunnels and Terminals.\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 61b7372bda5cde123a2b320ac92c1c24d2dfb9b1.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix crash with complex Integrated Scripting functions in writers\n\nFunctions returning an any type could be inserted into writers such as\nIntegrated Tunnels exporters, and type checking would incorrectly pass.\nThis could result in crashes where an incorrect value cast would occur.\nThis commit makes it so that ANY types will have an additional type\ncheck based on the actual determined value.\n\nCloses CyclopsMC/IntegratedScripting#20\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.23.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Optimize ingredient positions index lookups\n  Closes CyclopsMC/IntegratedTunnels#307\n\nFixes:\n* Fix Deepslate Dark Ore not being squeezable, Closes #1414\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.24.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add dedicated REI support, Closes CyclopsMC/IntegratedDynamics#1348\n* Restore Refined Storage integration\n* Restore Jade/Waila integration, Closes CyclopsMC/IntegratedDynamics#1413\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.24.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix cable placement with commands initializing networks\n  This was broken since CyclopsMC/IntegratedTunnels#243\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.24.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add error operator\n\nChanges:\n* Reduce network inits by half when placing cables, Closes #1439\n\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.24.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Reduce number of network events during init to improve performance, #1439\n  Modifying very large networks is a lot faster now.\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.24.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add lossy recipe to clear a facade in a crafting grid, Closes #1424\n\nFixes:\n* Fix client-server desync when applying part offsets, Closes #1448\n* Fix offset items only being applicable to 28 instead of 32, #1448\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.25.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add item tooltip operators\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.25.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add Item with tag operation\n\nFixes:\n* Fix REI recipe transfer to LP not working for fluids\n* Fix LP recipe transfer including chance-based outputs\n  Non-100% outputs could cause issues for autocrafting.\n  This is fixed for both JEI and REI.\n  Closes CyclopsMC/IntegratedCrafting#127\n* Fix tag-based JEI recipe transfer ignoring stack sizes\n  Closes CyclopsMC/IntegratedCrafting#128\n* Fix placing part before cable not properly connecting cables\n  Closes CyclopsMC/IntegratedTunnels#321\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.25.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix GUIs remaining open after external breakage, Closes #1472\n* Fix rare tooltip crash for item-based variable cards, Closes #1477\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.25.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Improve performance of block changes to very large networks\n  Pathfinder is ~70x faster, also improved tick speed with lots of devices (17 ms to 3 ms in test scene)\n\nFixes:\n* Fix duplication of offset enchancements when breaking cable, Closes #1480\n* Fix passive interaction changes not immediately applying on change\n  It used to only take effect after re-inserting a variable card.\n  Closes CyclopsMC/IntegratedDynamics#1470\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.25.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nChanges:\n* Avoid repeated log spam for forcefully unloaded parts, Closes #1481\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.25.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix NBT from int list not working for any lists\n  Closes CyclopsMC/IntegratedScripting#37\n* Fix typos in manual\n* Fixed number typo\n* Fixed variable description\n* Fixed typo in reader introduction\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.26.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add list set and multiset equality operators, Closes #1347\n* Add Fluid with tag operator, Closes #797\n* Add block and fluid tag operators, Closes #1394\n* Add Sqrt and Pow operators, Closes #1372\n* Add lectern and bookshelf support for infobook, Closes #1496\n\nFixes:\n* Fix non-fluid items being insertable into LP, Closes #1507\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.26.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix search hotkey also typing in hotkey in search box\n  Closes CyclopsMC/IntegratedTerminals#168\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Allow editing existing variables in Logic Programmer, Closes #357\n* Add Variable Value By ID operator aspect to network reader, Closes #1346\n* Add button to reset selected LP element\n\nChanges:\n* Don't drop item in world when closing LP\n* Improve LP element arrow\n\nFixes:\n* Fix operator text field in LP sometimes being red\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Only switch Wrench mode when aiming at air\n  Closes #1518\n  Closes #1390\n\nFixes:\n* Fix overlapping tooltips in LP write slot, Closes #1519\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix cables not being breakable with wrench, Closes #1523\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Return item to player instead of dropping on Labeler exit, Closes #1526\n\nFixes:\n* Fix fluidstack operator crash on empty items, Closes #1525\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix LP crash when loading with empty category card, Closes #1527\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix crash on load operator variable in LP, Closes #1537\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.27.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix memory leak when reloading offset variables, Closes #1549\n* Handle overflows in fluid reader total aspects, Closes #1547\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add fluid by name operator, Closes #1522\n\nChanges:\n* Ensure consistent order of ingredient types in LP, Closes #1539\n\nFixes:\n* Fix transparent facades having incorrect alpha value, Closes #1563\n* Fix lists materialization not applying to elements\n  This could cause issues when displaying lists of items from inventory\n  readers over Functional Storage Drawers.\n  Closes #1557\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix ticking network elements preventing chunk unloading, Closes #1567\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix read fluids not always updating in display panel, Closes #1570\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Temporarily disable chunk unload check\n  It is causing issues with delayers when worlds/chunks are reloaded.\n  Once a proper fix is available, this will be re-enabled.\n  Related to #1571\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Restore network element chunk unloading\n  This was temporarily disabled as it broke delayers and proxies on\n  world/chunk reload, which this commit fixes.\n  Closes #1571\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Temporarily disable chunk unload check again\n\nIt is causing issues with delayers after chunk reloaded.\nOnce a proper fix is available, this will be re-enabled.\n\nRelated to #1571\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.28.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Restore network element chunk unloading again, Closes #1571\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.29.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add squeezer recipes for clearing facades\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.29.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix facade squeezer recipes breaking infobook\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.29.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Add safety checks to disable some operators on infinite lists\n  Concretely, contains, reduce, and uniq are disabled on infinite lists.\n  Closes #1582\n\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.29.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add generic number casting operators\n  Closes CyclopsMC/IntegratedScripting#55\n  These new operators make it more convenient to work with return types of\n  Integrated Scripting, where numbers could be integers, doubles, or\n  longs.\n\nChanges:\n* Add safety check for infinite lists in list equals and itemstack count\n  Closes #1583\n  Related to #1582\n\nFixes:\n* Fix part states not being revalidated, Closes #1588\n* Fix oldest/newest mixup on delayer history in book, Closes #1592\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.30.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Document logical NBT expressions, CyclopsMC/CyclopsCore#210\n* Add config options for minimum update intervals for parts, Closes CyclopsMC/IntegratedTunnels#349\n* Add config option to log card IDs during evaluation, Closes #1345\n"
  },
  {
    "path": "resources/changelog/1.19.2-1.30.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add configurable energy production rate for coal generator (#1635), Closes #1634\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.14.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.3 or higher.\n\nInitial 1.19.3 release\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.14.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.3 or higher.\n\nChanges:\n* Restore JEI compat\n\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.14.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.3 or higher.\n\nFixes:\n* Fix JEI recipe modification after modifying in LP\n  Closes CyclopsMC/IntegratedCrafting#89\n\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.15.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.3 or higher.\n\nAdditions:\n* Allow recipe ingredients to be marked as reusable\n  Required for CyclopsMC/IntegratedCrafting#36\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.15.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.17.3 or higher.\n\nFixes:\n* Fix crash on filling recipes with JEI\n\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.15.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.0 or higher.\n\nChanges:\n* Use collapsed ingredient storage by default\n  Ingredient networks will now perform better for match-based lookups.\n  Related to CyclopsMC/IntegratedDynamics#1247\n\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.16.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Add support for static and dynamic offsets\n* Render offsets when holding wrench\n* Add modes to wrench to set offset and side of parts\n* Balance offsets using enhancement items"
  },
  {
    "path": "resources/changelog/1.19.3-1.16.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nAdditions:\n* Enable offsets in Redstone Writers\n\nChanges:\n* Optimize images\n"
  },
  {
    "path": "resources/changelog/1.19.3-1.16.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix delayer not propagating errors, Closes #1251\n* Fix delay error message not being shown in gui, Related to #1251\n\n"
  },
  {
    "path": "resources/changelog/1.19.4-1.16.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nInitial 1.19.4 release\n"
  },
  {
    "path": "resources/changelog/1.19.4-1.16.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix incompatibility with JEI API for 1.19.4, Closes #1267\n\n"
  },
  {
    "path": "resources/changelog/1.19.4-1.16.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Add IntegratedDynamicsSetupEvent\n  Required for resolving out-of-order event issues\n  Related to CyclopsMC/IntegratedTunnels#266\n\n"
  },
  {
    "path": "resources/changelog/1.19.4-1.16.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix offset render delay when crouching\n  Closes CyclopsMC/IntegratedTunnels#267\n\n"
  },
  {
    "path": "resources/changelog/1.19.4-1.16.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix double-shadow text printing in LP\n* Fix missing text colors in operator LP display\n* Fix Logic Programmer interface for lists of complex types\n  This makes it possible to makes lists of recipes, ingredients,\n  and operators.\n  Closes #641\n\n"
  },
  {
    "path": "resources/changelog/1.19.4-1.16.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.2 or higher.\n\nFixes:\n* Fix broken Dynamic Light Panels, Closes #1276\n* Fix mechanical recipes incorrectly being validated\n  This fixes Integrated Crafting crafters not working on them.\n  Closes CyclopsMC/IntegratedDynamics#1275\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.16.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix some logic programmer inputs being rendered incorrectly, Closes #1289\n* Fix invisible light crash in debug worlds, Closes #1287\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.16.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nInitial 1.20.1 release\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.16.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix random crash when placing squeezer, Closes #1282\n* Fix part comparator depending on state\n  This could cause problems where chunks would be reloaded during unloading.\n  This issue was introduced when part offsets were added.\n  Closes #1257\n* Fix focus between LP elements not being swapped correctly\n* Fix inability to remove text from recipes in LP, Closes #1280\n* Fix formatting error in operator cards tooltips, Closes #1281\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.16.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Give DOUBLE higher precedence than LONG for number operations, Closes #1284\n\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.17.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add generic variants of increment, decrement, and modulus\n  These were only usable for integers, but can now be used for all numbers.\n\nFixes:\n* Fix wrong error message for invalid applied operators, Closes #1293\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.18.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Expose getChannelSlotted in positioned addons network\n  Required for CyclopsMC/IntegratedTunnels#271\n\nFixes:\n* Fix rare crash when getting priority of moved network parts, Closes #1299\n* Fix crash when getting name of variable with ANY type, Closes #1301\n* Fix labeling text overlap in LP, Closes #1297\n* Fix backspace not working in delayer GUI, Closes #1298\n* Fix rare crash when loading positioned storages on high server load, Closes #1302\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.19.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Expose positions from channels\n  This is required for CyclopsMC/IntegratedCrafting#98\n\nFixes:\n* Fix predicate-based tunnels movement ignoring channels, Closes CyclopsMC/IntegratedTunnels#274\n* Fix crash on special operator with ANY type, Closes #1301\n* Fix Menril Tree replacing grass with dirt on stump place fail, Closes #1304\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.19.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix held items vanishing after gui close, Closes #1308\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.20.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add initialChange flag to storage change events\n  This is required to fix CyclopsMC/IntegratedCrafting#99\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.20.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix inputs of JEI recipes not being recognized\n  Closes CyclopsMC/IntegratedDynamics#1312\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.20.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix NBT.from_tag_list not working on Any lists, Closes #1315\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.20.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix predicate-based filtering interfaces causing ingredient loss\n  Integrated Tunnels filtering interaces could lose items\n  when using predicate-based filters.\n  Closes CyclopsMC/IntegratedTunnels#282\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.20.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix hard crash on replace_regex with invalid group, Closes #1317\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.21.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Expose interact names for operators\n  This is required for Integrated Scripting\n* Add apply_0 operator\n* Add apply_n operator\n\nChanges:\n* Add INetwork parameter to variable facade methods\n\nFixes:\n* Fix duplicate variable invalidate listeners being registered\n  This could leak to memory leaks for long-running multi-arg operators.\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.21.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix part offsets sometimes being delayed by one aspect update, Closes #1320\n* Fix errored offset variables not resetting after network reset, Closes #1321\n* Fix facade not being placeable on readers, Closes #1324\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.21.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix tps and ticktime aspects returning values different to Forge, Closes #1325\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.21.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Allow listening to EvaluationException resolutions, Required for CyclopsMC/IntegratedScripting#5\n\nFixes:\n* Fix crash for illegal item tag chars in recipe in LP, Closes CyclopsMC/IntegratedCrafting#103\n* Fix wrong ingredient slot positions in LP, Closes #1336\n* Fix Dank Storage item removal when placing cables, Closes #1332\n* Fix facades being craftable for non-solid blocks, Closes #1334, #1342\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.22.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Allow checking if there are variable-based offsets in API\n  Required for CyclopsMC/IntegratedTunnels#289\n\n\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.22.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Also copy label when copying variable cards, Closes #1354\n* Allow Ingredient.with* operators to have empty ingredient inputs\n* Allow entering items in logic programmer with larger stack sizes\n\nFixes:\n* Fix default labeller textfield being modifiable, Closes #1352\n* Fix apply_0 crashing on operators with input, Closes CyclopsMC/IntegratedScripting#11\n* Fix incorrect comparator in PrioritizedPartPos\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.22.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Interrupt speech before sending a new one to the narrator, Closes #1356\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add Terrablender compat for Meneglin biome\n\nFixes:\n* Fix menril trees not spawning in menril biome\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fixed Feature Order Cycle (FOC)\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix crash with complex Integrated Scripting functions in writers\n\nFunctions returning an any type could be inserted into writers such as\nIntegrated Tunnels exporters, and type checking would incorrectly pass.\nThis could result in crashes where an incorrect value cast would occur.\nThis commit makes it so that ANY types will have an additional type\ncheck based on the actual determined value.\n\nCloses CyclopsMC/IntegratedScripting#20\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Optimize ingredient positions index lookups\n  Closes CyclopsMC/IntegratedTunnels#307\n\nFixes:\n* Fix Deepslate Dark Ore not being squeezable, Closes #1414\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix variables dependent on offset aspects not always updating, Closes #1416\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Drop Part Offsets into their original form, Closes #1418\n\nFixes:\n* Fix creative batteries not providing energy, Closes #1421\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix op_by_name crashing if ResourceLocation is invalid, Closes #1381\n* Fix regex scan producing illegal lists for non-zero groups, Closes #1378\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Delay Terrablender registration\n  This fixes rare crashes when Terrablender was not yet fully initialized.\n  CyclopsMC/IntegratedDynamics#1385\n  Closes CyclopsMC/IntegratedDynamics#1388\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix JEI ghosts items not working for lists in the LP, Closes CyclopsMC/IntegratedDynamics#1398\n* Fix wrong sided part being shown in The One Probe, Closes #1401\n* Fix round-robin misbehaving with filtered interfaces, Closes CyclopsMC/IntegratedTunnels#302\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix broken biome aspect in block reader, Closes #1403\n* Fix incorrect type checking in complex reduce operation, Closes #1387\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Optimize ingredient positions index\n  This improves performance with Integrated Tunnels and Terminals.\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 61b7372bda5cde123a2b320ac92c1c24d2dfb9b1.\n  Closes CyclopsMC/IntegratedTerminals#134\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.23.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 8f53b7eb53302e5028d1b4414caf288d26eb73ee.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.24.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add dedicated REI support, Closes CyclopsMC/IntegratedDynamics#1348\n* Restore Refined Storage integration\n* Restore Jade/Waila integration, Closes CyclopsMC/IntegratedDynamics#1413\n\nFixes:\n* Fix cable placement with commands initializing networks\n  This was broken since CyclopsMC/IntegratedTunnels#243\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.24.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add error operator\n\nChanges:\n* Reduce network inits by half when placing cables, Closes #1439\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.24.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Reduce number of network events during init to improve performance, #1439\n  Modifying very large networks is a lot faster now.\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.24.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add lossy recipe to clear a facade in a crafting grid, Closes #1424\n\nFixes:\n* Fix client-server desync when applying part offsets, Closes #1448\n* Fix offset items only being applicable to 28 instead of 32, #1448\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add item tooltip operators\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nAdditions:\n* Add Item with tag operation\n\nFixes:\n* Fix REI recipe transfer to LP not working for fluids\n* Fix LP recipe transfer including chance-based outputs\n  Non-100% outputs could cause issues for autocrafting.\n  This is fixed for both JEI and REI.\n  Closes CyclopsMC/IntegratedCrafting#127\n* Fix tag-based JEI recipe transfer ignoring stack sizes\n  Closes CyclopsMC/IntegratedCrafting#128\n* Fix placing part before cable not properly connecting cables\n  Closes CyclopsMC/IntegratedTunnels#321\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix LP recipe slot properties not displaying tag tooltip items\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix GUIs remaining open after external breakage, Closes #1472\n* Fix rare tooltip crash for item-based variable cards, Closes #1477\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Improve performance of block changes to very large networks\n  Pathfinder is ~70x faster, also improved tick speed with lots of devices (17 ms to 3 ms in test scene)\n\nFixes:\n* Fix duplication of offset enchancements when breaking cable, Closes #1480\n* Fix passive interaction changes not immediately applying on change\n  It used to only take effect after re-inserting a variable card.\n  Closes CyclopsMC/IntegratedDynamics#1470\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nChanges:\n* Avoid repeated log spam for forcefully unloaded parts, Closes #1481\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.25.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.18.4 or higher.\n\nFixes:\n* Fix NBT from int list not working for any lists\n  Closes CyclopsMC/IntegratedScripting#37\n* Fix typos in manual\n* Fixed number typo\n* Fixed variable description\n* Fixed typo in reader introduction\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.26.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add list set and multiset equality operators, Closes #1347\n* Add Fluid with tag operator, Closes #797\n* Add block and fluid tag operators, Closes #1394\n* Add Sqrt and Pow operators, Closes #1372\n* Add lectern and bookshelf support for infobook, Closes #1496\n\nFixes:\n* Fix non-fluid items being insertable into LP, Closes #1507\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.26.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix search hotkey also typing in hotkey in search box\n  Closes CyclopsMC/IntegratedTerminals#168\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Allow editing existing variables in Logic Programmer, Closes #357\n* Add Variable Value By ID operator aspect to network reader, Closes #1346\n* Add button to reset selected LP element\n\nChanges:\n* Don't drop item in world when closing LP\n* Improve LP element arrow\n\nFixes:\n* Fix operator text field in LP sometimes being red\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix missing variable by id aspect texture\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Only switch Wrench mode when aiming at air\n  Closes #1518\n  Closes #1390\n\nFixes:\n* Fix overlapping tooltips in LP write slot, Closes #1519\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix cables not being breakable with wrench, Closes #1523\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Return item to player instead of dropping on Labeler exit, Closes #1526\n\nFixes:\n* Fix fluidstack operator crash on empty items, Closes #1525\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix LP crash when loading with empty category card, Closes #1527\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.27.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix crash on load operator variable in LP, Closes #1537\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.28.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add config option to disable cable collisions(#1538)\n\nFixes:\n* Properly handle long overflows in channel quantities\n\nInternal changes (required for Integrated Mekanism):\n* Support regexes in operators_output appendix\n* Abstract parts of recipe LP element\n* Make CommandTest more extensible\n* Make API less dependent on ModBase\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.28.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Improve offset gui error tooltip\n\nFixes:\n* Fix memory leak when reloading offset variables, Closes #1549\n* Handle overflows in fluid reader total aspects, Closes #1547\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add fluid by name operator, Closes #1522\n\nChanges:\n* Ensure consistent order of ingredient types in LP, Closes #1539\n\nFixes:\n* Fix transparent facades having incorrect alpha value, Closes #1563\n* Fix lists materialization not applying to elements\n  This could cause issues when displaying lists of items from inventory\n  readers over Functional Storage Drawers.\n  Closes #1557\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix ticking network elements preventing chunk unloading, Closes #1567\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Add IIngredientChannelInsertPreConsumer\n  Required for CyclopsMC/IntegratedCrafting#170\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add generic number casting operators\n  Closes CyclopsMC/IntegratedScripting#55\n  These new operators make it more convenient to work with return types of\n  Integrated Scripting, where numbers could be integers, doubles, or\n  longs.\n\nChanges:\n* Add safety check for infinite lists in list equals and itemstack count\n  Closes #1583\n  Related to #1582\n\nFixes:\n* Fix part states not being revalidated, Closes #1588\n* Fix oldest/newest mixup on delayer history in book, Closes #1592\n* Fix cable shape cache having cache collisions when there are two entries that have the same part on two different sides while having the same cable connections\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix read fluids not always updating in display panel, Closes #1570\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Temporarily disable chunk unload check\n  It is causing issues with delayers when worlds/chunks are reloaded.\n  Once a proper fix is available, this will be re-enabled.\n  Related to #1571\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Restore network element chunk unloading\n  This was temporarily disabled as it broke delayers and proxies on\n  world/chunk reload, which this commit fixes.\n  Closes #1571\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Temporarily disable chunk unload check again\n\nIt is causing issues with delayers after chunk reloaded.\nOnce a proper fix is available, this will be re-enabled.\n\nRelated to #1571\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Restore network element chunk unloading again, Closes #1571\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add squeezer recipes for clearing facades\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Add safety checks to disable some operators on infinite lists\n  Concretely, contains, reduce, and uniq are disabled on infinite lists.\n  Closes #1582\n\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.29.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Keep insertion order of network change observers\n  Required for CyclopsMC/IntegratedCrafting#112\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.30.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Document logical NBT expressions, CyclopsMC/CyclopsCore#210\n* Add config options for minimum update intervals for parts, Closes CyclopsMC/IntegratedTunnels#349\n* Add config option to log card IDs during evaluation, Closes #1345\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.30.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix Mono & Omni Directional connectors not consuming energy (#1631), Closes #1630\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.30.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nAdditions:\n* Add configurable energy production rate for coal generator (#1635), Closes #1634\n\nFixes:\n* Fix Variable Store, Proxy, Materializer, and Static Light Panel not consuming energy (#1633), Closes #1632\n* Fix energy battery texture overflow after NBT manipulation, Closes #1636 (#1637)\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.30.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Give Delayer its own energy consumption config separate from Proxy (#1641), Closes #1640\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.30.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Add BlockCable#getAppearance for connected textures on facades\n  This adds basic support to facades on cables for mods that add connected texture support.\n"
  },
  {
    "path": "resources/changelog/1.20.1-1.30.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix incorrect position of channel disabled tooltip in Integrated Crafting\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nInitial 1.20.4 release\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nFixes:\n* Fix Menril trees not being generated in other biomes\n\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nChanges:\n* Also copy label when copying variable cards, Closes #1354\n* Allow Ingredient.with* operators to have empty ingredient inputs\n* Allow entering items in logic programmer with larger stack sizes\n\nFixes:\n* Fix default labeller textfield being modifiable, Closes #1352\n* Fix apply_0 crashing on operators with input, Closes CyclopsMC/IntegratedScripting#11\n* Fix incorrect comparator in PrioritizedPartPos\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nFixes:\n* Fix crash when polling network capabilities client-side, Closes #294\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nFixes:\n* Fix crash when using referenced variables in store, Closes #1370\n\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nFixes:\n* Fix crash when printing squeezer and drying basin recipes, Closes CyclopsMC/IntegratedTunnels#296\n* Fix crash with Sodium when placing parts, Closes CyclopsMC/IntegratedTunnels#296\n\n"
  },
  {
    "path": "resources/changelog/1.20.4-1.21.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.0 or higher.\n\nFixes:\n* Fix Redstone Writers not always updating signals\n  Closes #1377\n  Closes #1382\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix Drying Basin not checking recipe inputs correctly, Closes #1369\n\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nInitial 1.21 release\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix crash when polling network capabilities client-side, Closes #294\n\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix incorrect Menril Leaves and Enriched Logs loot tables, Closes #1365\n* Fix missing fluid interaction sounds, Closes #1363\n* Fix variable model render crash when level is null, Closes #1366\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Interrupt speech before sending a new one to the narrator, Closes #1356\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix another variable model render crash when level is null, Closes #1366\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.21.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix errors in tag-based recipes\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.22.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nAdditions:\n* Add operators for interacting with item/fluid data components\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.22.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix crash when using referenced variables in store, Closes #1370\n"
  },
  {
    "path": "resources/changelog/1.21-1.22.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fix crash when printing squeezer and drying basin recipes, Closes CyclopsMC/IntegratedTunnels#296\n* Fix crash with Sodium when placing parts, Closes CyclopsMC/IntegratedTunnels#296\n* Fix crash when using referenced variables in store, Closes #1370\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.23.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nAdditions:\n* Add Terrablender compat for Meneglin biome\n\nFixes:\n* Fix menril trees not spawning in menril biome\n\n"
  },
  {
    "path": "resources/changelog/1.21-1.23.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.1 or higher.\n\nFixes:\n* Fixed Feature Order Cycle (FOC)\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Restore placement of levers on parts, Closes #1408\n* Fix crash when invalidating invalid network elements\n  This could occur when using AE2's Spatial IO.\n  Closes #1410\n* Fix unable to insert into part offset slots, Closes #1409\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nChanges:\n* Optimize ingredient positions index\n  This improves performance with Integrated Tunnels and Terminals.\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 61b7372bda5cde123a2b320ac92c1c24d2dfb9b1.\n  Closes CyclopsMC/IntegratedTerminals#134\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Revert \"Optimize ingredient positions index\"\n  This reverts commit 8f53b7eb53302e5028d1b4414caf288d26eb73ee.\n  Closes CyclopsMC/IntegratedTerminals#134\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.14.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Fix crash with complex Integrated Scripting functions in writers\n\nFunctions returning an any type could be inserted into writers such as\nIntegrated Tunnels exporters, and type checking would incorrectly pass.\nThis could result in crashes where an incorrect value cast would occur.\nThis commit makes it so that ANY types will have an additional type\ncheck based on the actual determined value.\n\nCloses CyclopsMC/IntegratedScripting#20\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.15.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nChanges:\n* Optimize ingredient positions index lookups\n  Closes CyclopsMC/IntegratedTunnels#307\n\nFixes:\n* Fix deadlock when getting light level of cable\n  This fixes an incompatibility with the Moonrise mod.\n  Closes #1415\n* Fix Deepslate Dark Ore not being squeezable, Closes #1414\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.16.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Fix variables dependent on offset aspects not always updating, Closes #1416\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.17.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nChanges:\n* Drop Part Offsets into their original form, Closes #1418\n\nFixes:\n* Fix creative batteries not providing energy, Closes #1421\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.18.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Fix silent error when deserializing empty item values, Closes #1422\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.19.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Fix capability crash when getting null direction from materializer, Closes #1423\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.19.11 or higher.\n\nChanges:\n* Update to updated CommonCapabilities API\n  Required for CyclopsMC/IntegratedDynamics#1375\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix Redstone Writers not always updating signals\n  Closes #1377\n  Closes #1382\n* Fix crash when getting facade colors for specific blocks, Closes #1380\n* Fix op_by_name crashing if ResourceLocation is invalid, Closes #1381\n* Fix regex scan producing illegal lists for non-zero groups, Closes #1378\n* Refer to NeoForge's updateJSONURL instead of Forge's\n* Fixed Feature Order Cycle (FOC)\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Delay Terrablender registration\n  This fixes rare crashes when Terrablender was not yet fully initialized.\n  CyclopsMC/IntegratedDynamics#1385\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Combine cable voxel shape components in getShape rather than getCollisionShape\n\nFixes:\n* Fix broken offset enchancement recipe, Closes #1367\n* Fix crash when placing Proxy or Materializer in SMP, Closes #1392\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix errors in conditional squeezer recipes, Closes CyclopsMC/CyclopsCore#191\n* Fix crash when placing creative energy battery, Closes #1397\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nChanges:\n* Improve performance by posting AttachCapabilitiesEventPart to other bus, Closes #1400\n\nFixes:\n* Fix JEI ghosts items not working for lists in the LP\n  Closes CyclopsMC/IntegratedDynamics#1398\n* Fix wrong sided part being shown in The One Probe, Closes #1401\n* Fix auto-supply on batteries not working, Closes #1399\n* Fix round-robin misbehaving with filtered interfaces, Closes CyclopsMC/IntegratedTunnels#302\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.20.0 or higher.\n\nFixes:\n* Fix crash when connecting cables with redstone with Sodium\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.23.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.1 or higher.\n\nFixes:\n* Fix incorrect type checking in complex reduce operation, Closes #1387\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.24.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add dedicated REI support, Closes CyclopsMC/IntegratedDynamics#1348\n* Restore Jade/Waila integration, Closes CyclopsMC/IntegratedDynamics#1413\n\nChanges:\n* Remove OnlyIn from appendHoverText and BlockCable\n\nFixes:\n* Fix network elements not being removed when broken as non-player\n  This fixes exceptions and console spam when removing cables with commands.\n  Related to #443\n* Fix server sometimes hanging after shutdown\n  Related to #1415\n* Fix cable placement with commands initializing networks\n  This was broken since CyclopsMC/IntegratedTunnels#243\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.24.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add error operator\n* Add nl_nl translations\n\nChanges:\n* Reduce network inits by half when placing cables, Closes #1439\n\nFixes:\n* Fix Jade integration not working, Closes #1438\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.24.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nChanges:\n* Add cs_cz translations\n\nFixes:\n* Reduce number of network events during init to improve performance, #1439\n  Modifying very large networks is a lot faster now.\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.24.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add lossy recipe to clear a facade in a crafting grid, Closes #1424\n\nChanges:\n* Reduce number of network events during init to improve performance, #1439\n\nFixes:\n* Fix client-server desync when applying part offsets, Closes #1448\n* Fix offset items only being applicable to 28 instead of 32, #1448\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add item tooltip operators\n\nFixes:\n* Fix incorrect player entity aspect description\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix crash when passing any list to Ingr.with_items, Closes #1457\n* Fix lighting issues when displaying items or ingredients, Closes #1458\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add ja_jp translations through Crowdin (#1492)\n\nFixes:\n* Fix NBT from int list not working for any lists\n  Closes CyclopsMC/IntegratedScripting#37\n* Fix typos in manual\n* Fixed number typo\n* Fixed variable description\n* Fixed typo in reader introduction\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add compostables, Closes #1498\n* Add stripped logs and woods tags, Closes #1500\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add skull-based note support to audio reader and writer\n\nFixes:\n* Fix wrench not removing cables after using off-hand item, Closes #1504\n* Fix proxies placed by non-players not having an id\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix conflicting item/fluid data value operator names, Closes #1462\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nChanges:\n* Add translations through Crowdin (#1455)\n\nFixes:\n* Fix REI recipe transfer to LP not working for fluids\n* Fix LP recipe transfer including chance-based outputs\n  Non-100% outputs could cause issues for autocrafting.\n  This is fixed for both JEI and REI.\n  Closes CyclopsMC/IntegratedCrafting#127\n* Fix tag-based JEI recipe transfer ignoring stack sizes\n  Closes CyclopsMC/IntegratedCrafting#128\n* Fix placing part before cable not properly connecting cables\n  Closes CyclopsMC/IntegratedTunnels#321\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix LP recipe slot properties not displaying tag tooltip items\n* Fix reusable tag-based recipes always having strict NBT\n  This would cause issues for autocrafting jobs where tag-based reusable\n  items that take damage would not be reused from the moment they take\n  damage.\n  Closes CyclopsMC/IntegratedCrafting#129\n* Fix cable model updating neighbours from wrong thread, Closes #1465\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix broken advancement icons\n* Fix broken looking at advancement trigger, #1471\n* Fix broken clear facade recipe\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix GUIs remaining open after external breakage, Closes #1472\n* Fix rare tooltip crash for item-based variable cards, Closes #1477\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nChanges:\n* Improve performance of block changes to very large networks\n  Pathfinder is ~70x faster, also improved tick speed with lots of devices (17 ms to 3 ms in test scene)\n* Remove reference to \"crystallised menril resin\"\n\nFixes:\n* Fix duplication of offset enchancements when breaking cable, Closes #1480\n* Fix passive interaction changes not immediately applying on change\n  It used to only take effect after re-inserting a variable card.\n  Closes CyclopsMC/IntegratedDynamics#1470\n* Fix tooltip typo for static light panel\n* Fix mention of \"degrees Kelvin\"\n* Fix \"raining\" grammar sentence in aspect\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Update ru_ru.json (#1483)\n\nChanges:\n* Avoid repeated log spam for forcefully unloaded parts, Closes #1481\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.25.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdded:\n* Add translations through Crowdin (#1475)\n  Co-authored-by: Crowdin Bot <support+bot@crowdin.com>\n\nFixes:\n* Fix crash for items in variables with stacksize > 99\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.26.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add list set and multiset equality operators, Closes #1347\n* Add Fluid with tag operator, Closes #797\n* Add block and fluid tag operators, Closes #1394\n* Add Sqrt and Pow operators, Closes #1372\n* Add lectern and bookshelf support for infobook, Closes #1496\n\nFixes:\n* Fix non-fluid items being insertable into LP, Closes #1507\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.26.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin\n\nFixes:\n* Fix search hotkey also typing in hotkey in search box\n  Closes CyclopsMC/IntegratedTerminals#168\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.26.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin\n\nFixes:\n* Fix cursor centering on gui switching, Closes CyclopsMC/IntegratedDynamics#1514\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Allow editing existing variables in Logic Programmer, Closes #357\n* Add Variable Value By ID operator aspect to network reader, Closes #1346\n* Add button to reset selected LP element\n\nChanges:\n* Don't drop item in world when closing LP\n* Improve LP element arrow\n\nFixes:\n* Fix operator text field in LP sometimes being red\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Fix missing variable by id aspect texture\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Reorder events to fix addon mod icons not loading\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Only switch Wrench mode when aiming at air\n  Closes #1518\n  Closes #1390\n\nFixes:\n* Fix overlapping tooltips in LP write slot, Closes #1519\n* Fix typos in language file\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1517)\n\nFixes:\n* Fix cables not being breakable with wrench, Closes #1523\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Return item to player instead of dropping on Labeler exit, Closes #1526\n\nFixes:\n* Fix fluidstack operator crash on empty items, Closes #1525\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1528)\n\nFixes:\n* Fix LP crash when loading with empty category card, Closes #1527\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Add PT_BR localization (#1535)\n\nFixes:\n* Fix crash on load operator variable in LP, Closes #1537\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1533)\n\nFixes:\n* Fix display panel being dark when on facade and under block, Closes #1531\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.27.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Fix block capabilities without block entity not being fetched\n  Related to CyclopsMC/IntegratedTunnels#346\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.28.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add Squeezer recipes for Wind Charges\n* Add config option to disable cable collisions(#1538)\n\nFixes:\n* Properly handle long overflows in channel quantities\n\nInternal changes: (required for Integrated Mekanism)\n* Support regexes in operators_output appendix\n* Abstract parts of recipe LP element\n* Make CommandTest more extensible\n* Make API less dependent on ModBase\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.28.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Improve offset gui error tooltip\n\nFixes:\n* Fix memory leak when reloading offset variables, Closes #1549\n* Handle overflows in fluid reader total aspects, Closes #1547\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1554)\n* Add fluid by name operator, Closes #1522\n\nChanges:\n* Ensure consistent order of ingredient types in LP, Closes #1539\n\nFixes:\n* Fix Grass Block Facades not having biome tints, Closes #1564\n* Fix transparent facades having incorrect alpha value, Closes #1563\n* Fix incorrect item (de)serialization when count is > 99, Closes #1558\n* Fix lists materialization not applying to elements\n  This could cause issues when displaying lists of items from inventory\n  readers over Functional Storage Drawers.\n  Closes #1557\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1566)\n\nFixes:\n* Fix ticking network elements preventing chunk unloading, Closes #1567\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1568)\n\nFixes:\n* Fix read fluids not always updating in display panel, Closes #1570\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Temporarily disable chunk unload check\n  It is causing issues with delayers when worlds/chunks are reloaded.\n  Once a proper fix is available, this will be re-enabled.\n  Related to #1571\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Restore network element chunk unloading\n  This was temporarily disabled as it broke delayers and proxies on\n  world/chunk reload, which this commit fixes.\n  Closes #1571"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Temporarily disable chunk unload check again\n\nIt is causing issues with delayers after chunk reloaded.\nOnce a proper fix is available, this will be re-enabled.\n\nRelated to #1571\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Restore network element chunk unloading again, Closes #1571\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.29.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Catch exceptions when deserializing invalid recipes\n\nThis could occur in Integrated Crafting after removing a mod for which a\nrecipe was stored.\n\nCloses CyclopsMC/CommonCapabilities#46\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.30.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add squeezer recipes for clearing facades\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.30.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Add safety checks to disable some operators on infinite lists\n  Concretely, contains, reduce, and uniq are disabled on infinite lists.\n  Closes #1582\n* Fix documentation on NBT Path field selector array indexing (#1580)\n  Closes #1579\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.30.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Keep insertion order of network change observers\n  Required for CyclopsMC/IntegratedCrafting#112\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.30.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1581)\n\nChanges:\n* Add IIngredientChannelInsertPreConsumer\n  Required for CyclopsMC/IntegratedCrafting#170\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.30.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add Almost Unified integration\n* Add generic number casting operators\n  Closes CyclopsMC/IntegratedScripting#55\n  These new operators make it more convenient to work with return types of\n  Integrated Scripting, where numbers could be integers, doubles, or\n  longs.\n\nChanges:\n* Add safety check for infinite lists in list equals and itemstack count\n  Closes #1583\n  Related to #1582\n\nFixes:\n* Fix part states not being revalidated, Closes #1588\n* Fix oldest/newest mixup on delayer history in book, Closes #1592\n* Fix cable shape cache having cache collisions when there are two entries that have the same part on two different sides while having the same cable connections\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.30.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add translations through Crowdin\n\nFixes:\n* Fix missing capability invalidation on modified parts, #1594\n\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.31.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add configurable pulse length property to Redstone Writer, Closes #724 (#1602)\n* Document logical NBT expressions, CyclopsMC/CyclopsCore#210\n* Add config options for minimum update intervals for parts, Closes CyclopsMC/IntegratedTunnels#349\n* Add config option to log card IDs during evaluation, Closes #1345\n* Add translations through Crowdin (#1598)\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add config option to force unloaded network elements to tick, Related to #1605, #1571, #1567\n* Add command for generating synthetic networks\n* Add command to measure network tick time\n\nFixes:\n* Fix parts sometimes becoming invalid, Closes #1605\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Fix ClassCastException in BlockCable when sapling grows nearby (#1623), Closes #1622\n* Fix tool_for_obsidian advancement triggering too often (#1620), Related to #1606\n* Fix VariablePredicate skipping getValue() when no value predicate is set (#1619)\n  Required for fixing game tests in CyclopsMC/IntegratedTunnels#354\n* Add game tests for all advancement criteria and fix broken advancements (#1617)\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Fix Mono & Omni Directional connectors not consuming energy (#1631), Closes #1630\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nAdditions:\n* Add configurable energy production rate for coal generator (#1635), Closes #1634\n\nFixes:\n* Fix Variable Store, Proxy, Materializer, and Static Light Panel not consuming energy (#1633), Closes #1632\n* Fix energy battery texture overflow after NBT manipulation, Closes #1636 (#1637)\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Give Delayer its own energy consumption config separate from Proxy (#1641), Closes #1640\n\nFixes:\n* Fix constant list Variable Card deserialization failure after world reload (#1639), Closes #1639\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nChanges:\n* Add BlockCable#getAppearance for connected textures on facades\n  This adds basic support to facades on cables for mods that add connected texture support.\n"
  },
  {
    "path": "resources/changelog/1.21.1-1.32.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.26.0 or higher.\n\nFixes:\n* Fix incorrect position of channel disabled tooltip in Integrated Crafting\n"
  },
  {
    "path": "resources/changelog/1.8-0.1.0.txt",
    "content": "Initial alpha release\nDon't use this in your survival world, things can and will break!\nIt is not guaranteed that your world will remain usable when updating this mod while in alpha.\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.2.0.txt",
    "content": "Make sure to backup your world before updating!\nRequires CyclopsCore version 0.5.0.\n\nFeatures:\n* Expose API\n* Add light panels\n* Add MCMultiPart support\n* Add Thaumcraft support\n* Add many aspects\n* Add many operators\n* Add Waila support\n* Add Materializer\n* Add Proxy\n* Add optional energy system\n\nChanges:\n* Improve display rendering\n* Improve part rendering\n* Add object value types\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.0.txt",
    "content": "Make sure to backup your world before updating!\nRequires CyclopsCore version 0.5.2.\n\nFeatures:\n* Add Charset integration\n* Add some more convenience inventory aspects\n* Add operators for null-checking\n* Add Fluid, ItemStack and Block value world renderers\n* Allow variable labeling in Logic Programmer. This can only be done when a Labeller is present in the player's inventory.\n* Add RF support, aspects and operators to get information from items\n* Add recipes for clearing and copying variables\n* Add operators to get hurt and death sounds of entities\n* Add operators to handle the oredict\n* Add operators to get block sounds\n* Add mounted entity and item frame operators\n* Add recipes for all parts\n* Add Audio Reader for reading note sounds\n* Add new advanced operators on entities\n* Add convenience clock read aspect with configurable pulse\n* Add effect writer with particle aspects\n* Add Audio Writer. It can play notes by id from five instrument with a configurable volume.\n* Add Charset Shifter writing aspects\n* Add worker capabilities for machines\n* Add worker capability aspects\n* Add Menril Tree, Fluid, Berries and Torches\n* Add Squeezer and Drying Basin\n* Add JEI support for the Squeezer and Drying Basin\n* Add Meneglin biome. It spawns Menril trees\n\nFixes:\n* Fix incorrect part disabling with lower frequency updating parts\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.1.txt",
    "content": "Make sure to backup your world before updating!\nRequires CyclopsCore version 0.5.2.\n\nFixes:\n* Fix crash when a metal-ore-adding-mod is available\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.2.txt",
    "content": "Make sure to backup your world before updating!\nRequires CyclopsCore version 0.5.2.\n\nFixes:\n* Fix null oredict entry insertion\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.3.txt",
    "content": "Make sure to backup your world before updating!\nRequires CyclopsCore version 0.5.3.\n\nAdditions:\n* Add cast from numbers to number, Closes #15\n* Allow searching in Logic Programmer by symbol, Closes #14\n\nChanges:\n* Make redstone parts less expensive, Closes #10\n* Reduce wild Menril Tree spawnrate, Closes #20, This also makes this rate configurable\n* Make taking items out of squeezers and drying basins easier, Closes #7\n\nFixes:\n* Fix MCMP-converted cables not always being removable\n* Fix crash when breaking a part after MCMP conversion, Closes #21\n* Fix elements not correctly being removed from networks when broken\n* Fix division by zero in redstone clock aspect, Closes #18\n* Fix inconsistent aspect property order, Closes #17\n* Fix missing cast operator translation\n* Add safety check for avoiding parts or states to be null, might solve #13\n* Fix Menril blocks not having a breaking tool, Closes #6\n* Fix part breaking crash without a wrench, Closes #16\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.5.\n\nFixes:\n* Correctly apply fluid colors during fluid rendering\n* Remove wood pulp squeezer recipe, might fix #22\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.5.g\n\nFixes:\n* Reduce Meneglin biome spawning rate and make it configurable, Closes #24\n\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.5.\n\nFixes:\n* Fix rare crash with RF API, Closes #16\n\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.5.\n\nFixes:\n* Fix Drying Basin and Squeezer sometimes not working anymore, Closes #28\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.6.\n\nFixes:\n* Fix zero itemstacks when copying variables, Closes #32\n\n"
  },
  {
    "path": "resources/changelog/1.8.9-0.3.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.7.\n\nFixes:\n* Fix ticking Energy Battery, Closes #38\n"
  },
  {
    "path": "resources/changelog/1.9-0.3.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.6.\n\nInitial 1.9 release\n"
  },
  {
    "path": "resources/changelog/1.9-0.3.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.6.\n\nFixes:\n* Reduce Meneglin biome spawning rate and make it configurable, Closes #24\n* Fix version checker link\n"
  },
  {
    "path": "resources/changelog/1.9-0.3.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.6.\n\nFixes:\n* Fix rare crash with RF API, Closes #16\n\n"
  },
  {
    "path": "resources/changelog/1.9-0.3.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.6.\n\nFixes:\n* Fix Drying Basin and Squeezer sometimes not working anymore, Closes #28\n"
  },
  {
    "path": "resources/changelog/1.9-0.3.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.8.\n\nFixes:\n* Fix zero itemstacks when copying variables, Closes #32\n\n"
  },
  {
    "path": "resources/changelog/1.9-0.3.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.5.9.\n\nFixes:\n* Fix ticking Energy Battery, Closes #38\n"
  },
  {
    "path": "resources/changelog/1.9.4-0.3.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.6.1.\n\nInitial 1.9.4 port\n"
  },
  {
    "path": "resources/changelog/1.9.4-0.4.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.7.0 and CommonCapabilities 1.0.0.\n\nWARNING: Changes to the fluid system has been made, so you will LOSE all Menril bucket items!\n\nAdditions:\n* Add operator valuetype\n  Includes logic programmer element to define operator value types via a autocomplete dropdown menu.\n* Implement reduce operator\n* Add list contains and contains-by-predicate operator\n* Add item and fluid predicate aspects to charset shifter writers\n* Implement flip operator. To flip the inputs of an operator.\n* Implement pipe operator. Chain outputs of operators to inputs.\n* Implement filter operator. Filters a list with the given predicate.\n  This commit also includes operators for combining predicates with conjunction, disjunction and negation.\n* Implement map operator\n  It takes an operator and a list, and it applies the operator lazily on all elements of the list.\n* Add currying support for the apply operator\n* Implement apply operator\n  This operator takes an operator value and another value, and tries to apply the value to the operator.\n* Add sponge recipe to drying basin\n* Add Tesla capabilities\n* Add IC2 EU reader aspects and operators\n* Add temperature readers\n* Add Forestry recipe integration. Menril Resin can now be created in the Forestry Squeezer\n* Add EvilCraft undead log and leaves squeezin recipes\n* Add TCon convenience recipes. Crystalized Menril Block crafting can now be done in the TCon Casting Basin.\n* Add Logic Programmer power-user features\n  Allow scrolling and selecting elements with keyboard.\n  Arrow keys, tab, return and enter are supported.\n  Alt+F will focus the searcfield.\n  Alt+R will show/save-hide the label field if a labeler is present.\n* Use the IWrench capability for the wrench\n* Add Charset fluid-shifter support\n\nChanges:\n* Many changes to improve ticking and rendering efficiency\n* Return to main part gui after changing part settings\n* Always focus textbox when in Labeller gui\n* Improve error handling in LazyExpression\n* Use new fluid capabilities for items. The custom menril bucket has been removed in favor of the universal bucket.\n* Show Squeezer instructions in tooltip, Closes #44\n\nFixes:\n* Fix display part rotation only happening client-side, Closes #58\n* Fix fuel progress in generator gui not always being correct, Closes #55\n* Fix guis staying open after breaking the block/part, Closes #53\n* Fix MCMP part parts always facing down\n* Improve variable card models, Closes #9\n* Fix Meneglin biomes spawning too frequently, Closes #24\n* Fix cable rendering not updating when adding/removing facades\n* Fix invisible light block from light panels staying after panel removal, Closes #40\n* Fix values that exist for one tick not being shown in display parts, Closes #46\n* Fix cable models not updating when disconnecting or changing neighbours\n* Fix crash when interacting with parts when network got corrupted, Closes #25\n* Fix rare crash when playing sounds\n* Fix change of return value for entity sounds in 1.9\n* Fix mounted entities operator\n* Fix failing integration tests\n* Add itemstack comparator ignoring damage values\n* Fix redstone writer deactivation not working correctly, Closes #49\n* Correctly show blockstate name, instead of the block name, Closes #47\n* Improve lighting in display rendering, Closes #45\n* Fix localization for player list aspect in world reader, Closes #48\n* Fix player aspect in entity reader reading all players from the world, instead of the target\n* Make property retrieval during rendering safer, Closes #50\n"
  },
  {
    "path": "resources/changelog/1.9.4-0.4.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.7.0.\n\nAdditions:\n* Create zh_CN.lang, huge thanks to @CannonFotter, @3TUSK, @bakaxyf, @Meow-J, @exzhawk and @ayasaki-hayate\n"
  },
  {
    "path": "resources/changelog/1.9.4-0.4.2.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.7.0 or higher.\n\nAdditions:\n* Add The One Probe integration\n* Add list (not) empty operators\n\nChanges:\n* Remove debug prints\n* Don't emit particles from the effect writer when the input value is less than zero\n* Add distinction between mainhand and offhand for held item operator\n* Allow empty string variable creation\n* Deselect LP element on escape or left arrow\n* Allow conversion of numbers to strings\n\nFixes:\n* Fix multipart crash on aspects with properties\n* Fix part placement sounds not working\n* Fix localization for round, ceil and floor\n* Fix lighting issues with display parts\n* Fix selecting LP search box not working\n"
  },
  {
    "path": "resources/changelog/1.9.4-0.5.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.7.6 or higher.\n\nAdditions:\n* Add Network Diagnostics to profile networks, highlight parts and teleport to them.\n  Run /integrateddynamics networkdiagnostics to open it.\n* Add filled Menril Log variant, they drop menril logs\n\nChanges:\n* Texture overhaul by @Yulife\n* Make squeezer and variable transformer recipes easier\n* Update Drying Basin and Logic Cable recipes for texture consistency\n* Improve logic programmer escape behaviour\n* Remove server warning when removing part that is not attached to a cable\n* Don't drop parts and cables in creative mode\n* Disable networks after a crash\n* Rename Cable to Logic Cable\n\nFixes:\n* Fix broken Menril Sapling model\n* Fix torches can be placed on all sides of the squeezer and drying basin\n* Fix squeezer insta-squeezing items when it was squeezed before\n* Trigger block render update when removing cables with attached parts\n* Fix parts not dropping in corrupted networks\n* Fix network corruption after server crash.\n* Make cable state retrieval safe for client desyncs\n* Make getCapability safer\n* Fix render crash while logging in, Closes #61 and closes #62\n"
  },
  {
    "path": "resources/changelog/1.9.4-0.5.1.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 0.7.7 or higher.\n\nChanges:\n* Improve ValueType textures\n* Improve Menril Tree stump generation\n\nFixes:\n* Fix non-backwards compatibility with new Menril Log variants, Closes #65\n* Update Tesla support to its new lowercase mod id\n\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.10.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1663)\n\nFixes:\n* Fix Menril Saplings not growing with bonemeal, Closes #1664\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Remove old Tesla references from language files\n* Fix battery capacity not persisting after world rejoin/restart (#1667)\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.3.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nInitial 26.1.1 release\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.4.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nChanges:\n* Restore JEI and Terrablender mod compats\n\nFixes:\n* Fix constant list Variable Card deserialization failure after world reload (#1639), Closes #1639\n* Give Delayer its own energy consumption config separate from Proxy (#1641), Closes #1640\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.5.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nAdditions:\n* Add c:clumps tag to Crystalized Menril Chunk (#1659), Closes #1656\n* Add translations through Crowdin (#1649)\n\nFixes:\n* Fix Menril Resin and Liquid Chorus fluids being non-interactable in world (#1658), Closes #1657\n* Fix drying basin stalling when both item and fluid inputs are present (#1655)\n* Fix drying basin not consuming items when right-clicked with a stack (#1654), Closes #1652\n* Fix incorrect item model for Menril Sapling\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.6.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nChanges:\n* Add leaf particle colors for Menril Leaves\n\nFixes:\n* Fix crash when rendering empty battery item stack (#1661)\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.7.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix worldgen: prevent menril trees from generating on top of other trees (#1662), Closes #1660\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.8.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nChanges:\n* Add BlockCable#getAppearance for connected textures on facades\n  This adds basic support to facades on cables for mods that add connected texture support.\n"
  },
  {
    "path": "resources/changelog/26.1.1-1.32.9.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.25.5 or higher.\n\nFixes:\n* Fix NPE in EnergyStorageItemBlockEnergyContainer when itemAccess is null (#1665)\n"
  },
  {
    "path": "resources/changelog/26.1.2-1.32.11.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.29.5 or higher.\n\nInitial 26.1.2 release\n"
  },
  {
    "path": "resources/changelog/26.1.2-1.32.12.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.29.5 or higher.\n\nFixes:\n* Fix incorrect position of channel disabled tooltip in Integrated Crafting\n"
  },
  {
    "path": "resources/changelog/26.1.2-1.32.13.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.29.5 or higher.\n\nFixes:\n* Fix Crafting Interface crash on Mechanical Drying Basin, Closes CyclopsMC/IntegratedCrafting#197\n\n"
  },
  {
    "path": "resources/changelog/26.1.2-1.33.0.txt",
    "content": "As always, don't forget to backup your world before updating!\nRequires CyclopsCore version 1.29.5 or higher.\n\nAdditions:\n* Add translations through Crowdin (#1671)\n* Update Refined Storage compat to v2, Closes CyclopsMC/IntegratedDynamics#1544\n"
  },
  {
    "path": "scripts/pre-commit",
    "content": "#!/bin/sh\nstagedFiles=$(git diff --staged --name-only)\n\necho \"Formatting code...\"\n./gradlew spotlessApply\n\nfor file in $stagedFiles; do\n  if test -f \"$file\"; then\n    git add $file\n  fi\ndone\n"
  },
  {
    "path": "secrets.properties_template",
    "content": "libs_path=[path_to_jar_dependencies (see forge.gradle)]\n"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        mavenLocal()\n        gradlePluginPortal()\n        maven { url = 'https://maven.neoforged.net/releases' }\n    }\n}\n\nplugins {\n    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/Capabilities.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.neoforge.capabilities.BlockCapability;\nimport net.neoforged.neoforge.capabilities.ItemCapability;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.capability.IngredientComponentCapability;\nimport org.cyclops.integrateddynamics.api.block.IDynamicLight;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.api.block.IFacadeable;\nimport org.cyclops.integrateddynamics.api.block.IVariableContainer;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.evaluate.IValueInterface;\nimport org.cyclops.integrateddynamics.api.ingredient.capability.IPositionedAddonsNetworkIngredientsHandler;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHolder;\nimport org.cyclops.integrateddynamics.api.network.*;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartCapability;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\n\n/**\n * Used capabilities for this mod.\n * @author rubensworks\n */\npublic class Capabilities {\n    public static final class Cable {\n        public static final BlockCapability<ICable, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"cable\"), ICable.class);\n    }\n\n    public static final class CableFakeable {\n        public static final BlockCapability<ICableFakeable, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"cable_fakeable\"), ICableFakeable.class);\n    }\n\n    public static final class DynamicLight {\n        public static final BlockCapability<IDynamicLight, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"dynamic_light\"), IDynamicLight.class);\n    }\n\n    public static final class DynamicRedstone {\n        public static final BlockCapability<IDynamicRedstone, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"dynamic_redstone\"), IDynamicRedstone.class);\n    }\n\n    public static final class Facadeable {\n        public static final BlockCapability<IFacadeable, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"facadeable\"), IFacadeable.class);\n    }\n\n    public static final class NetworkCarrier {\n        public static final BlockCapability<INetworkCarrier, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"network_carrier\"), INetworkCarrier.class);\n    }\n\n    public static final class NetworkElementProvider {\n        public static final BlockCapability<INetworkElementProvider, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"network_element_provider\"), INetworkElementProvider.class);\n    }\n\n    public static final class PartContainer {\n        public static final BlockCapability<IPartContainer, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"part_container\"), IPartContainer.class);\n    }\n\n    public static final class PathElement {\n        public static final BlockCapability<IPathElement, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"path_element\"), IPathElement.class);\n        public static final PartCapability<IPathElement> PART = PartCapability.create(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"path_element\"), IPathElement.class);\n    }\n\n    public static final class VariableFacade {\n        public static final ItemCapability<IVariableFacadeHolder, Void> ITEM = ItemCapability.createVoid(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"variable_facade_holder\"), IVariableFacadeHolder.class);\n    }\n\n    public static final class PositionedAddonsNetworkIngredientsHandler {\n        public static final IngredientComponentCapability<IPositionedAddonsNetworkIngredientsHandler, Void> INGREDIENT = IngredientComponentCapability.createVoid(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_addons_network_ingredients_handler\"), IPositionedAddonsNetworkIngredientsHandler.class);\n    }\n\n    public static final class ValueInterface {\n        public static final BlockCapability<IValueInterface, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"value_interface\"), IValueInterface.class);\n        public static final PartCapability<IValueInterface> PART = PartCapability.create(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"value_interface\"), IValueInterface.class);\n    }\n\n    public static final class VariableContainer {\n        public static final BlockCapability<IVariableContainer, Direction> BLOCK = BlockCapability.createSided(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"variable_container\"), IVariableContainer.class);\n        public static final PartCapability<IVariableContainer> PART = PartCapability.create(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"variable_container\"), IVariableContainer.class);\n    }\n\n    public static final class EnergyNetwork {\n        public static final NetworkCapability<IEnergyNetwork> NETWORK = NetworkCapability.create(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"energy_network\"), IEnergyNetwork.class);\n    }\n\n    public static final class PartNetwork {\n        public static final NetworkCapability<IPartNetwork> NETWORK = NetworkCapability.create(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"part_network\"), IPartNetwork.class);\n    }\n\n    public static final class Energy {\n        public static final NetworkCapability<EnergyHandler> NETWORK = NetworkCapability.create(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"energy_storage\"), EnergyHandler.class);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/Configs.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport org.cyclops.cyclopscore.config.ConfigHandlerCommon;\nimport org.cyclops.integrateddynamics.advancement.criterion.*;\nimport org.cyclops.integrateddynamics.block.*;\nimport org.cyclops.integrateddynamics.blockentity.*;\nimport org.cyclops.integrateddynamics.component.*;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTickingConfig;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerAspectSettingsConfig;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartOffsetConfig;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartSettingsConfig;\nimport org.cyclops.integrateddynamics.core.recipe.category.RecipeBookCategoryDryingBasinConfig;\nimport org.cyclops.integrateddynamics.core.recipe.category.RecipeBookCategoryMechanicalDryingBasinConfig;\nimport org.cyclops.integrateddynamics.core.recipe.category.RecipeBookCategoryMechanicalSqueezerConfig;\nimport org.cyclops.integrateddynamics.core.recipe.category.RecipeBookCategorySqueezerConfig;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplayDryingBasinConfig;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplaySqueezerConfig;\nimport org.cyclops.integrateddynamics.core.recipe.type.*;\nimport org.cyclops.integrateddynamics.entity.item.EntityItemTargettedConfig;\nimport org.cyclops.integrateddynamics.fluid.FluidLiquidChorusConfig;\nimport org.cyclops.integrateddynamics.fluid.FluidMenrilResinConfig;\nimport org.cyclops.integrateddynamics.gametest.fuzzing.FuzzingGameTestInstanceConfig;\nimport org.cyclops.integrateddynamics.gametest.integration.IntegrationMethodGameTestInstanceConfig;\nimport org.cyclops.integrateddynamics.inventory.container.*;\nimport org.cyclops.integrateddynamics.item.*;\nimport org.cyclops.integrateddynamics.loot.conditions.LootConditionMatchWrenchConfig;\nimport org.cyclops.integrateddynamics.loot.functions.*;\nimport org.cyclops.integrateddynamics.recipe.*;\nimport org.cyclops.integrateddynamics.sound.SoundEventEffectPageFlipMultipleConfig;\nimport org.cyclops.integrateddynamics.sound.SoundEventEffectPageFlipSingleConfig;\nimport org.cyclops.integrateddynamics.world.gen.foliageplacer.FoliagePlacerMenrilConfig;\nimport org.cyclops.integrateddynamics.world.gen.trunkplacer.TrunkPlacerMenrilConfig;\n\n/**\n * This class holds a set of all the configs that need to be registered.\n * @author rubensworks\n */\npublic class Configs {\n\n    public static void registerBlocks(ConfigHandlerCommon configHandler) {\n\n        // Fluids\n        configHandler.addConfigurable(new FluidMenrilResinConfig());\n        configHandler.addConfigurable(new FluidLiquidChorusConfig());\n\n        // Tile entities\n        configHandler.addConfigurable(new BlockEntityCoalGeneratorConfig());\n        configHandler.addConfigurable(new BlockEntityDelayConfig());\n        configHandler.addConfigurable(new BlockEntityDryingBasinConfig());\n        configHandler.addConfigurable(new BlockEntityEnergyBatteryConfig());\n        configHandler.addConfigurable(new BlockEntityMaterializerConfig());\n        configHandler.addConfigurable(new BlockEntityMechanicalDryingBasinConfig());\n        configHandler.addConfigurable(new BlockEntityMechanicalSqueezerConfig());\n        configHandler.addConfigurable(new BlockEntityMultipartTickingConfig());\n        configHandler.addConfigurable(new BlockEntityProxyConfig());\n        configHandler.addConfigurable(new BlockEntitySqueezerConfig());\n        configHandler.addConfigurable(new BlockEntityVariableStoreConfig());\n\n        // Blocks\n        configHandler.addConfigurable(new BlockCableConfig());\n        configHandler.addConfigurable(new BlockVariablestoreConfig());\n        configHandler.addConfigurable(new BlockLogicProgrammerConfig());\n        configHandler.addConfigurable(new BlockInvisibleLightConfig());\n        configHandler.addConfigurable(new BlockEnergyBatteryConfig());\n        configHandler.addConfigurable(new BlockCreativeEnergyBatteryConfig());\n        configHandler.addConfigurable(new BlockCoalGeneratorConfig());\n        configHandler.addConfigurable(new BlockProxyConfig());\n        configHandler.addConfigurable(new BlockMaterializerConfig());\n        configHandler.addConfigurable(new BlockMenrilLogConfig());\n        configHandler.addConfigurable(new BlockMenrilLogStrippedConfig());\n        configHandler.addConfigurable(new BlockMenrilWoodConfig());\n        configHandler.addConfigurable(new BlockMenrilWoodStrippedConfig());\n        configHandler.addConfigurable(new BlockMenrilLogFilledConfig());\n        configHandler.addConfigurable(new BlockMenrilLeavesConfig());\n        configHandler.addConfigurable(new BlockMenrilSaplingConfig());\n        configHandler.addConfigurable(new BlockMenrilPlanksConfig());\n        configHandler.addConfigurable(new BlockMenrilSlabConfig());\n        configHandler.addConfigurable(new BlockMenrilFenceConfig());\n        configHandler.addConfigurable(new BlockMenrilFenceGateConfig());\n        configHandler.addConfigurable(new BlockCrystalizedMenrilBlockConfig());\n        configHandler.addConfigurable(new BlockCrystalizedMenrilBrickConfig());\n        configHandler.addConfigurable(new BlockFluidMenrilResinConfig());\n        configHandler.addConfigurable(new BlockDryingBasinConfig());\n        configHandler.addConfigurable(new BlockSqueezerConfig());\n        configHandler.addConfigurable(new BlockMenrilDoorConfig());\n        configHandler.addConfigurable(new BlockMenrilTorchWallConfig());\n        configHandler.addConfigurable(new BlockMenrilTorchConfig());\n        configHandler.addConfigurable(new BlockMenrilTorchStoneWallConfig());\n        configHandler.addConfigurable(new BlockMenrilTorchStoneConfig());\n        configHandler.addConfigurable(new BlockMenrilPlanksStairsConfig());\n        configHandler.addConfigurable(new BlockCrystalizedMenrilBlockStairsConfig());\n        configHandler.addConfigurable(new BlockCrystalizedMenrilBrickStairsConfig());\n        configHandler.addConfigurable(new BlockDelayConfig());\n        configHandler.addConfigurable(new BlockFluidLiquidChorusConfig());\n        configHandler.addConfigurable(new BlockCrystalizedChorusBlockConfig());\n        configHandler.addConfigurable(new BlockCrystalizedChorusBrickConfig());\n        configHandler.addConfigurable(new BlockCrystalizedChorusBlockStairsConfig());\n        configHandler.addConfigurable(new BlockCrystalizedChorusBrickStairsConfig());\n        configHandler.addConfigurable(new BlockMechanicalSqueezerConfig());\n        configHandler.addConfigurable(new BlockMechanicalDryingBasinConfig());\n        configHandler.addConfigurable(new BlockCrystalizedMenrilBlockSlabConfig());\n        configHandler.addConfigurable(new BlockCrystalizedMenrilBrickSlabConfig());\n        configHandler.addConfigurable(new BlockCrystalizedChorusBlockSlabConfig());\n        configHandler.addConfigurable(new BlockCrystalizedChorusBrickSlabConfig());\n\n        // Items\n        configHandler.addConfigurable(new ItemBucketLiquidChorusConfig());\n        configHandler.addConfigurable(new ItemBucketMenrilResinConfig());\n        configHandler.addConfigurable(new ItemWrenchConfig());\n        configHandler.addConfigurable(new ItemVariableConfig());\n        configHandler.addConfigurable(new ItemLabellerConfig());\n        configHandler.addConfigurable(new ItemFacadeConfig());\n        configHandler.addConfigurable(new ItemCrystalizedMenrilChunkConfig());\n        configHandler.addConfigurable(new ItemVariableTransformerConfig(true));\n        configHandler.addConfigurable(new ItemVariableTransformerConfig(false));\n        configHandler.addConfigurable(new ItemMenrilBerriesConfig());\n        configHandler.addConfigurable(new ItemPortableLogicProgrammerConfig());\n        configHandler.addConfigurable(new ItemOnTheDynamicsOfIntegrationConfig());\n        configHandler.addConfigurable(new ItemCrystalizedChorusChunkConfig());\n        configHandler.addConfigurable(new ItemLogicDirectorConfig());\n        configHandler.addConfigurable(new ItemProtoChorusConfig());\n        configHandler.addConfigurable(new ItemEnhancementOffsetConfig());\n\n        // World features\n        configHandler.addConfigurable(new FoliagePlacerMenrilConfig());\n        configHandler.addConfigurable(new TrunkPlacerMenrilConfig());\n\n        // Entities\n        configHandler.addConfigurable(new EntityItemTargettedConfig());\n\n        // Guis\n        configHandler.addConfigurable(new ContainerAspectSettingsConfig());\n        configHandler.addConfigurable(new ContainerCoalGeneratorConfig());\n        configHandler.addConfigurable(new ContainerDelayConfig());\n        configHandler.addConfigurable(new ContainerLabellerConfig());\n        configHandler.addConfigurable(new ContainerLogicProgrammerConfig());\n        configHandler.addConfigurable(new ContainerLogicProgrammerPortableConfig());\n        configHandler.addConfigurable(new ContainerMaterializerConfig());\n        configHandler.addConfigurable(new ContainerMechanicalDryingBasinConfig());\n        configHandler.addConfigurable(new ContainerMechanicalSqueezerConfig());\n        configHandler.addConfigurable(new ContainerOnTheDynamicsOfIntegrationConfig());\n        configHandler.addConfigurable(new ContainerPartDisplayConfig());\n        configHandler.addConfigurable(new ContainerPartReaderConfig());\n        configHandler.addConfigurable(new ContainerPartSettingsConfig());\n        configHandler.addConfigurable(new ContainerPartOffsetConfig());\n        configHandler.addConfigurable(new ContainerPartWriterConfig());\n        configHandler.addConfigurable(new ContainerProxyConfig());\n        configHandler.addConfigurable(new ContainerVariablestoreConfig());\n\n        // Recipe types\n        configHandler.addConfigurable(new RecipeTypeDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeTypeMechanicalDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeTypeSqueezerConfig());\n        configHandler.addConfigurable(new RecipeTypeMechanicalSqueezerConfig());\n\n        // Recipe book categories\n        configHandler.addConfigurable(new RecipeBookCategoryDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeBookCategoryMechanicalDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeBookCategorySqueezerConfig());\n        configHandler.addConfigurable(new RecipeBookCategoryMechanicalSqueezerConfig());\n\n        // Recipe displays\n        configHandler.addConfigurable(new RecipeDisplayDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeDisplaySqueezerConfig());\n\n        // Recipes\n        configHandler.addConfigurable(new RecipeSerializerDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeSerializerMechanicalDryingBasinConfig());\n        configHandler.addConfigurable(new RecipeSerializerSqueezerConfig());\n        configHandler.addConfigurable(new RecipeSerializerMechanicalSqueezerConfig());\n        configHandler.addConfigurable(new RecipeSerializerSqueezerFacadeConfig());\n        configHandler.addConfigurable(new RecipeSerializerMechanicalSqueezerFacadeConfig());\n        configHandler.addConfigurable(new RecipeSerializerNbtClearConfig());\n        configHandler.addConfigurable(new RecipeEnergyContainerCombinationConfig());\n        configHandler.addConfigurable(new ItemVariableCopyRecipeConfig());\n        configHandler.addConfigurable(new ItemFacadeRecipeConfig());\n        configHandler.addConfigurable(new RecipeSerializerCraftingSpecialShapedOmniDirectionalConfig());\n        configHandler.addConfigurable(new RecipeSerializerCraftingSpecialShapedOmniDirectional3Config());\n        configHandler.addConfigurable(new RecipeSerializerCraftingSpecialShapelessOmniDirectionalConfig());\n\n        // Criterion triggers\n        configHandler.addConfigurable(new NetworkInitializedTriggerConfig());\n        configHandler.addConfigurable(new PartReaderAspectTriggerConfig());\n        configHandler.addConfigurable(new PartVariableDrivenAspectTriggerConfig());\n        configHandler.addConfigurable(new PartWriterAspectTriggerConfig());\n        configHandler.addConfigurable(new VariableCreatedTriggerConfig());\n\n        // Loot conditions\n        configHandler.addConfigurable(new LootConditionMatchWrenchConfig());\n\n        // Loot functions\n        configHandler.addConfigurable(new LootFunctionCopyEnergyBatteryDataConfig());\n        configHandler.addConfigurable(new LootFunctionCopyMechanicalDryingBasinTanksConfig());\n        configHandler.addConfigurable(new LootFunctionCopyMechanicalMachineEnergyConfig());\n        configHandler.addConfigurable(new LootFunctionCopyMechanicalSqueezerTankConfig());\n        configHandler.addConfigurable(new LootFunctionCopyProxyIdConfig());\n\n        // Sounds\n        configHandler.addConfigurable(new SoundEventEffectPageFlipMultipleConfig());\n        configHandler.addConfigurable(new SoundEventEffectPageFlipSingleConfig());\n\n        // Data components\n        configHandler.addConfigurable(new DataComponentActivatedConfig());\n        configHandler.addConfigurable(new DataComponentFacadeBlockConfig());\n        configHandler.addConfigurable(new DataComponentFluidContentInOutConfig());\n        configHandler.addConfigurable(new DataComponentOmnidirectionalGroupConfig());\n        configHandler.addConfigurable(new DataComponentPartEnhancementConfig());\n        configHandler.addConfigurable(new DataComponentPartStateConfig());\n        configHandler.addConfigurable(new DataComponentProxyIdConfig());\n        configHandler.addConfigurable(new DataComponentVariableFacadeConfig());\n        configHandler.addConfigurable(new DataComponentWrenchTargetBlockPosConfig());\n        configHandler.addConfigurable(new DataComponentWrenchTargetDirectionConfig());\n        configHandler.addConfigurable(new DataComponentWrenchModeConfig());\n\n        // Game test instances\n        configHandler.addConfigurable(new IntegrationMethodGameTestInstanceConfig(IntegratedDynamics._instance));\n        configHandler.addConfigurable(new FuzzingGameTestInstanceConfig(IntegratedDynamics._instance));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/GeneralConfig.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.IdentifierException;\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.fml.event.config.ModConfigEvent;\nimport org.apache.logging.log4j.Level;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.ModConfigLocation;\nimport org.cyclops.cyclopscore.config.extendedconfig.DummyConfigCommon;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * A config with general options for this mod.\n * @author rubensworks\n *\n */\npublic class GeneralConfig extends DummyConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If an anonymous mod startup analytics request may be sent to our analytics service.\")\n    public static boolean analytics = true;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If the version checker should be enabled.\")\n    public static boolean versionChecker = true;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The default update frequency in ticks to use for new parts.\", minimalValue = 1, configLocation = ModConfigLocation.SERVER)\n    public static int defaultPartUpdateFreq = 1;\n\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The energy usage multiplier for networks.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int energyConsumptionMultiplier = 0;\n\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the audio reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int audioReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the audio writer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int audioWriterBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the block reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int blockReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the mono-directional connector.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int connectorMonoDirectionalBaseConsumption = 32;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the omni-directional connector.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int connectorOmniDirectionalBaseConsumption = 128;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the effect writer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int effectWriterBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the entity reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int entityReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the entity writer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int entityWriterBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the extra-dimensional reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int extraDimensionalReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the fluid reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int fluidReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the inventory reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int inventoryReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the inventory writer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int inventoryWriterBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the machine reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int machineReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the machine writer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int machineWriterBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the materializer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int materializerBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the network reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int networkReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the display panel when it has a variable.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int panelDisplayBaseConsumptionEnabled = 2;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the display panel when does not have a variable.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int panelDisplayBaseConsumptionDisabled = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the dynamic light panel.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int panelLightDynamicBaseConsumption = 0;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the static light panel.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int panelLightStaticBaseConsumption = 0;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the delayer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int delayerBaseConsumption = 2;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the proxy.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int proxyBaseConsumption = 2;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the redstone reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int redstoneReaderBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the redstone writer.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int redstoneWriterBaseConsumption = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the variable store.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int variablestoreBaseConsumption = 4;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The base energy usage for the world reader.\", minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int worldReaderBaseConsumption = 1;\n\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The maximum render distance for part overlays to render. The higher, the more resource intensive.\", isCommandable = true, minimalValue = 1, configLocation = ModConfigLocation.CLIENT)\n    public static int partOverlayRenderdistance = 15;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If cable models should be cached for rendering optimization.\", isCommandable = true, configLocation = ModConfigLocation.CLIENT)\n    public static boolean cacheCableModels = true;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The maximum network energy transfer rate.\", isCommandable = true, minimalValue = 0, configLocation = ModConfigLocation.SERVER)\n    public static int energyRateLimit = Integer.MAX_VALUE;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The maximum offset in blocks a directional connector can look for its target.\", minimalValue = 1, configLocation = ModConfigLocation.SERVER)\n    public static int maxDirectionalConnectorOffset = 512;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The NBT tags that are not allowed to be read by displaying NBT tags or performing operations on them.\", configLocation = ModConfigLocation.SERVER)\n    public static List<String> nbtTagBlacklist = Lists.newArrayList(); // Tag names that have to be ignored\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"When enabled, networks will stop ticking and values will not be shown and evaluated again. This can be used to fix crashing networks by temporarily enabling this option.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static boolean safeMode = false;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The fastest possible frequency in ticks at which ingredient network should be observed.\", minimalValue = 1, isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static int ingredientNetworkObserverFrequencyMin = 5;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The slowest possible frequency in ticks at which ingredient network should be observed.\", minimalValue = 1, isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static int ingredientNetworkObserverFrequencyMax = 40;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The ingredient network observation frequency slowdown rate in ticks.\", minimalValue = 1, isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static int ingredientNetworkObserverFrequencyIncreaseFactor = 1;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The ingredient network observation frequency slowdown rate in ticks.\", minimalValue = 1, isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static int ingredientNetworkObserverFrequencyDecreaseFactor = 5;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The frequency in ticks at which ingredient network should be observed after a position's contents are changed.\", minimalValue = 0, isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static int ingredientNetworkObserverFrequencyForced = 0;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The number of threads that the ingredient network observer can use.\", minimalValue = 1, requiresMcRestart = true, configLocation = ModConfigLocation.SERVER)\n    public static int ingredientNetworkObserverThreads = 4;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If the ingredient network observer can work on separate thread.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static boolean ingredientNetworkObserverEnableMultithreading = true;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If network change events should be logged. Only enable this when debugging.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static boolean logChangeEvents = false;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If variable card IDs should be logged during evaluation. This is useful for debugging crashes caused by card evaluation.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static boolean logCardEvaluation = false;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"How deep the recursion stack on an operator can become. This is to avoid game crashes when building things like the omega operator.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static int operatorRecursionLimit = 256;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"Priority list of mod id's when determining tag-based recipe outputs.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static List<String> recipeTagOutputModPriorities = Lists.newArrayList();\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"If corrupted networks should automatically be restored on first tick.\", isCommandable = true, configLocation = ModConfigLocation.SERVER)\n    public static boolean recreateCorruptedNetworks = true;\n\n    @ConfigurablePropertyCommon(category = \"core\", comment = \"The maximum frequency at which speach messages can be played in milliseconds.\", isCommandable = true, configLocation = ModConfigLocation.CLIENT)\n    public static int speachMaxFrequency = 1000;\n\n    @ConfigurablePropertyCommon(category = \"core\" , comment = \"When true, use the LONG number format style. Otherwise, use the SHORT style.\")\n    public static boolean numberCompactUseLongStyle = false;\n\n    @ConfigurablePropertyCommon(category = \"core\" , comment = \"The maximum number of fractional digits to include in the result of the compact operator\")\n    public static int numberCompactMaximumFractionDigits = 2;\n\n    @ConfigurablePropertyCommon(category = \"core\" , comment = \"The minimum number of fractional digits to include in the result of the compact operator\")\n    public static int numberCompactMinimumFractionDigits = 0;\n\n    @ConfigurablePropertyCommon(category = \"core\" , comment = \"The maximum number of integer digits to include in the result of the compact operator\")\n    public static int numberCompactMaximumIntegerDigits = 3;\n\n    @ConfigurablePropertyCommon(category = \"core\" , comment = \"The minimum number of integer digits to include in the result of the compact operator\")\n    public static int numberCompactMinimumIntegerDigits = 1;\n\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The default port for running the network diagnostics HTTP server.\", configLocation = ModConfigLocation.CLIENT)\n    public static int diagnosticsWebServerPort = 3030;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The maximum offset in blocks a part can target.\", minimalValue = 1, configLocation = ModConfigLocation.SERVER)\n    public static int maxPartOffset = 32;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The distance from which part offsets should be shown.\", minimalValue = 1, configLocation = ModConfigLocation.SERVER)\n    public static int partOffsetRenderDistance = 16;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The maximum values that Part Offset items will have when dropped from a broken part.\", minimalValue = 1, configLocation = ModConfigLocation.SERVER)\n    public static int enchancementOffsetPartDropValue = 4;\n\n    @ConfigurablePropertyCommon(category = \"machine\" , comment = \"When true, disable the collision for cable.\", configLocation = ModConfigLocation.SERVER)\n    public static boolean disableCableCollision = false;\n\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The minimum update interval to enforce for all parts, in number of ticks.\", configLocation = ModConfigLocation.SERVER)\n    public static int partsMinimumUpdateInterval = 1;\n    @ConfigurablePropertyCommon(category = \"general\", comment = \"The minimum update intervals to enforce for specific parts. You can add entries in the form of 'integrateddynamics:machine_reader:10', where '10' refers to the number of ticks.\", configLocation = ModConfigLocation.SERVER)\n    public static List<String> partMinimumUpdateIntervals = Lists.newArrayList();\n    public static Map<IPartType<?, ?>, Integer> partMinimumUpdateIntervalsMap = Maps.newIdentityHashMap();\n\n    @ConfigurablePropertyCommon(category = \"core\" , comment = \"When true, network elements will tick, irrespective of whether their chunk is loaded or not.\", configLocation = ModConfigLocation.SERVER, isCommandable = true)\n    public static boolean tickUnloadedNetworkElements = false;\n\n    public GeneralConfig() {\n        super(IntegratedDynamics._instance, \"general\");\n        IntegratedDynamics._instance.getModEventBus().addListener(this::onConfigLoad);\n        IntegratedDynamics._instance.getModEventBus().addListener(this::onConfigReload);\n    }\n\n    public void onConfigLoad(ModConfigEvent.Loading event) {\n        if (Objects.equals(event.getConfig().getModId(), Reference.MOD_ID)) {\n            recreatePartMinimumUpdateIntervals();\n        }\n    }\n\n    public void onConfigReload(ModConfigEvent.Loading event) {\n        if (Objects.equals(event.getConfig().getModId(), Reference.MOD_ID)) {\n            recreatePartMinimumUpdateIntervals();\n        }\n    }\n\n    private void recreatePartMinimumUpdateIntervals() {\n        partMinimumUpdateIntervalsMap = Maps.newIdentityHashMap();\n        for (String entry : partMinimumUpdateIntervals) {\n            int lastColon = entry.lastIndexOf(\":\");\n            if (lastColon == -1) {\n                IntegratedDynamics.clog(Level.WARN, \"Error while reloading config entry partMinimumUpdateIntervals: Invalid entry \" + entry);\n                continue;\n            }\n            String partType = entry.substring(0, lastColon);\n            int interval;\n            try {\n                interval = Integer.parseInt(entry.substring(lastColon + 1));\n            } catch (NumberFormatException e) {\n                IntegratedDynamics.clog(Level.WARN, \"Error while reloading config entry partMinimumUpdateIntervals: Invalid interval integer in \" + entry);\n                continue;\n            }\n            try {\n                IPartType<?, ?> part = PartTypes.REGISTRY.getPartType(Identifier.tryParse(partType));\n                if (part != null) {\n                    partMinimumUpdateIntervalsMap.put(part, interval);\n                } else {\n                    IntegratedDynamics.clog(Level.WARN, \"Error while reloading config entry partMinimumUpdateIntervals: Could not find a part with id \" + partType);\n                }\n            } catch (IdentifierException e) {\n                IntegratedDynamics.clog(Level.WARN, \"Error while reloading config entry partMinimumUpdateIntervals: \" + e.getMessage());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/IntegratedDynamics.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport net.minecraft.commands.CommandBuildContext;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.core.Registry;\nimport net.minecraft.gametest.framework.TestEnvironmentDefinition;\nimport net.minecraft.world.item.CreativeModeTab;\nimport net.minecraft.world.item.ItemStack;\nimport net.neoforged.bus.api.EventPriority;\nimport net.neoforged.bus.api.IEventBus;\nimport net.neoforged.fml.ModList;\nimport net.neoforged.fml.common.Mod;\nimport net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;\nimport net.neoforged.fml.javafmlmod.FMLModContainer;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.event.RegisterGameTestsEvent;\nimport net.neoforged.neoforge.event.server.ServerStartedEvent;\nimport net.neoforged.neoforge.registries.NewRegistryEvent;\nimport org.apache.logging.log4j.Level;\nimport org.cyclops.cyclopscore.config.ConfigHandlerCommon;\nimport org.cyclops.cyclopscore.gametest.GameTestLoaderHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.infobook.IInfoBookRegistry;\nimport org.cyclops.cyclopscore.infobook.InfoBookRegistry;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.persist.world.GlobalCounters;\nimport org.cyclops.cyclopscore.proxy.IClientProxy;\nimport org.cyclops.cyclopscore.proxy.ICommonProxy;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProviderRegistry;\nimport org.cyclops.integrateddynamics.api.client.render.part.IPartOverlayRendererRegistry;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRendererRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueCastRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeLightLevelRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeRegistry;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementTypeRegistry;\nimport org.cyclops.integrateddynamics.api.network.INetworkCraftingHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.part.IPartTypeRegistry;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRegistry;\nimport org.cyclops.integrateddynamics.capability.ingredient.IngredientComponentCapabilities;\nimport org.cyclops.integrateddynamics.capability.network.NetworkCapabilityConstructors;\nimport org.cyclops.integrateddynamics.client.render.part.PartOverlayRendererRegistry;\nimport org.cyclops.integrateddynamics.client.render.part.PartOverlayRenderers;\nimport org.cyclops.integrateddynamics.client.render.valuetype.ValueTypeWorldRendererRegistry;\nimport org.cyclops.integrateddynamics.client.render.valuetype.ValueTypeWorldRenderers;\nimport org.cyclops.integrateddynamics.command.CommandCrash;\nimport org.cyclops.integrateddynamics.command.CommandGenerateNetwork;\nimport org.cyclops.integrateddynamics.command.CommandNetworkDiagnostics;\nimport org.cyclops.integrateddynamics.command.CommandTest;\nimport org.cyclops.integrateddynamics.core.NoteBlockEventReceiver;\nimport org.cyclops.integrateddynamics.core.TickHandler;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviderRegistry;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviders;\nimport org.cyclops.integrateddynamics.core.evaluate.DelayVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.core.evaluate.ProxyVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.OperatorRegistry;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.event.IntegratedDynamicsSetupEvent;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlerRegistry;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\nimport org.cyclops.integrateddynamics.core.item.VariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypeRegistry;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypes;\nimport org.cyclops.integrateddynamics.core.network.NetworkCraftingHandlerRegistry;\nimport org.cyclops.integrateddynamics.core.part.PartTypeRegistry;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\nimport org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;\nimport org.cyclops.integrateddynamics.gametest.*;\nimport org.cyclops.integrateddynamics.gametest.integration.GameTester;\nimport org.cyclops.integrateddynamics.infobook.OnTheDynamicsOfIntegrationBook;\nimport org.cyclops.integrateddynamics.item.ItemOnTheDynamicsOfIntegrationConfig;\nimport org.cyclops.integrateddynamics.metadata.RegistryExportables;\nimport org.cyclops.integrateddynamics.part.PartTypeConnectorOmniDirectional;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.cyclops.integrateddynamics.proxy.ClientProxy;\nimport org.cyclops.integrateddynamics.proxy.CommonProxy;\n\nimport java.lang.reflect.Field;\n\n/**\n * The main mod class of IntegratedDynamics.\n * @author rubensworks\n *\n */\n@Mod(Reference.MOD_ID)\npublic class IntegratedDynamics extends ModBaseNeoForge<IntegratedDynamics> {\n\n    /**\n     * The unique instance of this mod.\n     */\n    public static IntegratedDynamics _instance;\n\n    public static GlobalCounters.Access globalCounters = null;\n\n    public IntegratedDynamics(IEventBus modEventBus) {\n        super(Reference.MOD_ID, (instance) -> _instance = instance, modEventBus);\n\n        // Register world storages\n        registerWorldStorage(NetworkWorldStorage.Access.getInstance(this));\n        registerWorldStorage(globalCounters = new GlobalCounters.Access(this));\n        registerWorldStorage(LabelsWorldStorage.Access.getInstance(this));\n\n        getRegistryManager().addRegistry(IVariableFacadeHandlerRegistry.class, VariableFacadeHandlerRegistry.getInstance());\n        getRegistryManager().addRegistry(IValueTypeRegistry.class, ValueTypeRegistry.getInstance());\n        getRegistryManager().addRegistry(IValueCastRegistry.class, ValueCastRegistry.getInstance());\n        getRegistryManager().addRegistry(IValueTypeListProxyFactoryTypeRegistry.class, ValueTypeListProxyFactoryTypeRegistry.getInstance());\n        getRegistryManager().addRegistry(IValueTypeLightLevelRegistry.class, ValueTypeLightLevelRegistry.getInstance());\n        getRegistryManager().addRegistry(IPartTypeRegistry.class, PartTypeRegistry.getInstance());\n        getRegistryManager().addRegistry(IAspectRegistry.class, AspectRegistry.getInstance());\n        getRegistryManager().addRegistry(IOperatorRegistry.class, OperatorRegistry.getInstance());\n        getRegistryManager().addRegistry(ILogicProgrammerElementTypeRegistry.class, LogicProgrammerElementTypeRegistry.getInstance());\n        if(getModHelpers().getMinecraftHelpers().isClientSide()) {\n            getRegistryManager().addRegistry(IPartOverlayRendererRegistry.class, PartOverlayRendererRegistry.getInstance());\n            getRegistryManager().addRegistry(IValueTypeWorldRendererRegistry.class, ValueTypeWorldRendererRegistry.getInstance());\n            getRegistryManager().addRegistry(IVariableModelProviderRegistry.class, VariableModelProviderRegistry.getInstance());\n        }\n\n        getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).registerHandler(ProxyVariableFacadeHandler.getInstance());\n        getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).registerHandler(DelayVariableFacadeHandler.getInstance());\n        getRegistryManager().addRegistry(IInfoBookRegistry.class, new InfoBookRegistry());\n        getRegistryManager().addRegistry(IIngredientComponentHandlerRegistry.class, IngredientComponentHandlerRegistry.getInstance());\n        getRegistryManager().addRegistry(INetworkCraftingHandlerRegistry.class, NetworkCraftingHandlerRegistry.getInstance());\n\n        // Preload parts, so we force their blocks and items to be registered\n        PartTypes.load();\n\n        modEventBus.addListener(EventPriority.LOWEST, IngredientComponentHandlers::onIngredientComponentsPopulated);\n        modEventBus.addListener(this::onRegistriesCreate);\n        modEventBus.register(new NetworkCapabilityConstructors());\n        modEventBus.addListener(this::registerIntegrationGameTests);\n\n        NeoForge.EVENT_BUS.addListener(this::onServerStartedLoadedGroups);\n        NeoForge.EVENT_BUS.register(TickHandler.getInstance());\n        NeoForge.EVENT_BUS.register(NoteBlockEventReceiver.getInstance());\n    }\n\n    public void onRegistriesCreate(NewRegistryEvent event) {\n        IngredientComponentCapabilities.load();\n    }\n\n    @Override\n    protected LiteralArgumentBuilder<CommandSourceStack> constructBaseCommand(Commands.CommandSelection selection, CommandBuildContext context) {\n        LiteralArgumentBuilder<CommandSourceStack> root = super.constructBaseCommand(selection, context);\n\n        root.then(CommandCrash.make());\n        root.then(CommandGenerateNetwork.make());\n        root.then(CommandNetworkDiagnostics.make());\n        root.then(CommandTest.make());\n\n        return root;\n    }\n\n    @Override\n    protected void setup(FMLCommonSetupEvent event) {\n        ValueTypes.load();\n        IngredientComponentHandlers.load();\n        ValueCastMappings.load();\n        ValueTypeLightLevels.load();\n        ValueTypeListProxyFactories.load();\n        Operators.load();\n        Aspects.load();\n        PartTypes.register();\n        LogicProgrammerElementTypes.load();\n        RegistryExportables.load();\n\n        // Register info book\n        putGenericReference(ModBaseNeoForge.REFKEY_INFOBOOK_REWARDS, ItemOnTheDynamicsOfIntegrationConfig.bookRewards);\n        getRegistryManager().getRegistry(IInfoBookRegistry.class).registerInfoBook(\n                OnTheDynamicsOfIntegrationBook.getInstance(), \"/data/\" + Reference.MOD_ID + \"/info/on_the_dynamics_of_integration.xml\");\n\n        IntegratedDynamicsSetupEvent integratedDynamicsSetupEvent = new IntegratedDynamicsSetupEvent(this.getContainer());\n        ModList.get().forEachModContainer((name, container) -> {\n            if (container instanceof FMLModContainer fmlModContainer) {\n                fmlModContainer.getEventBus().post(integratedDynamicsSetupEvent);\n            }\n        });\n\n        if(IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            PartOverlayRenderers.load();\n            ValueTypeWorldRenderers.load();\n            VariableModelProviders.load();\n        }\n\n        super.setup(event);\n    }\n\n    protected void onServerStartedLoadedGroups(ServerStartedEvent event) {\n        PartTypeConnectorOmniDirectional.LOADED_GROUPS.onStartedEvent(event);\n    }\n\n    @Override\n    protected IClientProxy constructClientProxy() {\n        return new ClientProxy();\n    }\n\n    @Override\n    protected ICommonProxy constructCommonProxy() {\n        return new CommonProxy();\n    }\n\n    @Override\n    protected CreativeModeTab.Builder constructDefaultCreativeModeTab(CreativeModeTab.Builder builder) {\n        return super.constructDefaultCreativeModeTab(builder)\n                .icon(() -> new ItemStack(RegistryEntries.ITEM_CABLE));\n    }\n\n    @Override\n    protected void onConfigsRegister(ConfigHandlerCommon configHandler) {\n        super.onConfigsRegister(configHandler);\n\n        configHandler.addConfigurable(new GeneralConfig());\n        Configs.registerBlocks(configHandler);\n    }\n\n    @Override\n    public Class<?>[] getGameTestClasses() {\n        return new Class[]{\n                GameTestsAdvancements.class,\n                GameTestsAspectsReadAudio.class,\n                GameTestsAspectsReadBlock.class,\n                GameTestsAspectsReadEntity.class,\n                GameTestsAspectsReadExtraDimensional.class,\n                GameTestsAspectsReadFluid.class,\n                GameTestsAspectsReadInventory.class,\n                GameTestsAspectsReadMachine.class,\n                GameTestsAspectsReadNetwork.class,\n                GameTestsAspectsReadRedstone.class,\n                GameTestsAspectsReadWorld.class,\n                GameTestsAspectsWriteRedstone.class,\n                GameTestsBattery.class,\n                GameTestsCombinedAspects.class,\n                GameTestsDelayer.class,\n                GameTestsDryingBasin.class,\n                GameTestsFacades.class,\n                GameTestsFluids.class,\n                GameTestsFuzzing.class,\n                GameTestsGenerator.class,\n                GameTestsMaterializer.class,\n                GameTestsMechanicalDryingBasin.class,\n                GameTestsMechanicalSqueezer.class,\n                GameTestsNetwork.class,\n                GameTestsOffsets.class,\n                GameTestsParts.class,\n                GameTestsPerformance.class,\n                GameTestsProxy.class,\n                GameTestsSqueezer.class,\n                GameTestsWrench.class,\n        };\n    }\n\n    protected void registerIntegrationGameTests(RegisterGameTestsEvent event) {\n        if (!GameTestLoaderHelpers.areGameTestsEnabled(getModId())) {\n            return;\n        }\n        try {\n            Field field = RegisterGameTestsEvent.class.getDeclaredField(\"environmentsRegistry\");\n            field.setAccessible(true);\n            Registry<TestEnvironmentDefinition<?>> testEnvironmentRegistry = (Registry<TestEnvironmentDefinition<?>>) field.get(event);\n            GameTester.registerCommonTests(getModId(), event::registerTest, testEnvironmentRegistry);\n            GameTestsFuzzing.registerCommonTests(getModId(), event::registerTest, testEnvironmentRegistry);\n        } catch (NoSuchFieldException | IllegalAccessException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    /**\n     * Log a new info message for this mod.\n     * @param message The message to show.\n     */\n    public static void clog(String message) {\n        IntegratedDynamics._instance.log(Level.INFO, message);\n    }\n\n    /**\n     * Log a new message of the given level for this mod.\n     * @param level The level in which the message must be shown.\n     * @param message The message to show.\n     */\n    public static void clog(Level level, String message) {\n        IntegratedDynamics._instance.log(level, message);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/Reference.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport org.cyclops.cyclopscore.helper.IModHelpers;\n\n/**\n * Class that can hold basic static things that are better not hard-coded\n * like mod details, texture paths, ID's...\n * @author rubensworks\n */\npublic final class Reference {\n\n    // Mod info\n    public static final String MOD_ID = \"integrateddynamics\";\n    public static final String GA_TRACKING_ID = \"UA-65307010-4\";\n    public static final String VERSION_URL = \"https://raw.githubusercontent.com/CyclopsMC/Versions/master/\" + IModHelpers.get().getMinecraftHelpers().getMinecraftVersionMajorMinor() + \"/IntegratedDynamics.txt\";\n    public static final String BOOK_URL = \"https://integrateddynamics.rubensworks.net/book/\";\n\n    // Paths\n    public static final String TEXTURE_PATH_GUI = \"textures/gui/\";\n\n    // MOD ID's\n    public static final String MOD_FORGE = \"forge\";\n    public static final String MOD_CYCLOPSCORE = \"cyclopscore\";\n    public static final String MOD_COMMONCAPABILITIES = \"commoncapabilities\";\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/RegistryEntries.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.component.DataComponentType;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.sounds.SoundEvent;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.crafting.RecipeBookCategory;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.biome.Biome;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacerType;\nimport net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;\nimport net.minecraft.world.level.material.FlowingFluid;\nimport net.minecraft.world.level.material.Fluid;\nimport net.neoforged.neoforge.fluids.SimpleFluidContent;\nimport net.neoforged.neoforge.registries.DeferredHolder;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.block.*;\nimport org.cyclops.integrateddynamics.blockentity.*;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerAspectSettings;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartSettings;\nimport org.cyclops.integrateddynamics.core.item.ItemBlockEnergyContainer;\nimport org.cyclops.integrateddynamics.core.recipe.type.*;\nimport org.cyclops.integrateddynamics.entity.item.EntityItemTargetted;\nimport org.cyclops.integrateddynamics.inventory.container.*;\nimport org.cyclops.integrateddynamics.item.ItemEnhancement;\nimport org.cyclops.integrateddynamics.item.ItemFacade;\nimport org.cyclops.integrateddynamics.item.ItemVariable;\nimport org.cyclops.integrateddynamics.item.ItemWrench;\nimport org.cyclops.integrateddynamics.recipe.ItemFacadeRecipe;\nimport org.cyclops.integrateddynamics.recipe.ItemVariableCopyRecipe;\nimport org.cyclops.integrateddynamics.world.gen.foliageplacer.FoliagePlacerMenril;\nimport org.cyclops.integrateddynamics.world.gen.trunkplacer.TrunkPlacerMenril;\n\n/**\n * Referenced registry entries.\n * @author rubensworks\n */\npublic class RegistryEntries {\n\n    public static final DeferredHolder<Item, Item> ITEM_BUCKET_LIQUID_CHORUS = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:bucket_liquid_chorus\"));\n    public static final DeferredHolder<Item, Item> ITEM_BUCKET_MENRIL_RESIN = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:bucket_menril_resin\"));\n    public static final DeferredHolder<Item, Item> ITEM_CABLE = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:cable\"));\n    public static final DeferredHolder<Item, Item> ITEM_CRYSTALIZED_MENRIL_CHUNK = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:crystalized_menril_chunk\"));\n    public static final DeferredHolder<Item, Item> ITEM_DELAY = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:delay\"));\n    public static final DeferredHolder<Item, ItemBlockEnergyContainer> ITEM_ENERGY_BATTERY = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:energy_battery\"));\n    public static final DeferredHolder<Item, ItemFacade> ITEM_FACADE = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:facade\"));\n    public static final DeferredHolder<Item, Item> ITEM_ON_THE_DYNAMICS_OF_INTEGRATION = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:on_the_dynamics_of_integration\"));\n    public static final DeferredHolder<Item, Item> ITEM_LABELLER = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:labeller\"));\n    public static final DeferredHolder<Item, Item> ITEM_PORTABLE_LOGIC_PROGRAMMER = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:portable_logic_programmer\"));\n    public static final DeferredHolder<Item, Item> ITEM_PROXY = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:proxy\"));\n    public static final DeferredHolder<Item, ItemVariable> ITEM_VARIABLE = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:variable\"));\n    public static final DeferredHolder<Item, Item> ITEM_WRENCH = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:wrench\"));\n    public static final DeferredHolder<Item, Item> ITEM_MENRIL_TORCH = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:menril_torch\"));\n    public static final DeferredHolder<Item, Item> ITEM_MENRIL_TORCH_STONE = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:menril_torch_stone\"));\n    public static final DeferredHolder<Item, ItemEnhancement> ITEM_ENHANCEMENT_OFFSET = DeferredHolder.create(Registries.ITEM, Identifier.parse(\"integrateddynamics:enhancement_offset\"));\n\n    public static final DeferredHolder<Block, BlockCable> BLOCK_CABLE = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:cable\"));\n    public static final DeferredHolder<Block, Block> BLOCK_COAL_GENERATOR = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:coal_generator\"));\n    public static final DeferredHolder<Block, Block> BLOCK_CRYSTALIZED_MENRIL_BRICK = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:crystalized_menril_brick\"));\n    public static final DeferredHolder<Block, Block> BLOCK_CRYSTALIZED_MENRIL_BLOCK = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:crystalized_menril_block\"));\n    public static final DeferredHolder<Block, Block> BLOCK_CRYSTALIZED_CHORUS_BRICK = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:crystalized_chorus_brick\"));\n    public static final DeferredHolder<Block, Block> BLOCK_CRYSTALIZED_CHORUS_BLOCK = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:crystalized_chorus_block\"));\n    public static final DeferredHolder<Block, Block> BLOCK_DELAY = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:delay\"));\n    public static final DeferredHolder<Block, BlockDryingBasin> BLOCK_DRYING_BASIN = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:drying_basin\"));\n    public static final DeferredHolder<Block, BlockEnergyBattery> BLOCK_ENERGY_BATTERY = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:energy_battery\"));\n    public static final DeferredHolder<Block, BlockEnergyBattery> BLOCK_ENERGY_BATTERY_CREATIVE = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:energy_battery_creative\"));\n    public static final DeferredHolder<Block, BlockFluidLiquidChorus> BLOCK_FLUID_LIQUID_CHORUS = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:block_liquid_chorus\"));\n    public static final DeferredHolder<Block, BlockFluidMenrilResin> BLOCK_FLUID_MENRIL_RESIN = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:block_menril_resin\"));\n    public static final DeferredHolder<Block, Block> BLOCK_INVISIBLE_LIGHT = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:invisible_light\"));\n    public static final DeferredHolder<Block, Block> BLOCK_LOGIC_PROGRAMMER = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:logic_programmer\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MATERIALIZER = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:materializer\"));\n    public static final DeferredHolder<Block, BlockMechanicalDryingBasin> BLOCK_MECHANICAL_DRYING_BASIN = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:mechanical_drying_basin\"));\n    public static final DeferredHolder<Block, BlockMechanicalSqueezer> BLOCK_MECHANICAL_SQUEEZER = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:mechanical_squeezer\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_LEAVES = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_leaves\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_LOG = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_log\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_LOG_FILLED = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_log_filled\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_LOG_STRIPPED = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_log_stripped\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_WOOD = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_wood\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_WOOD_STRIPPED = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_wood_stripped\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_PLANKS = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_planks\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_PLANKS_STAIRS = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_planks_stairs\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_SAPLING = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_sapling\"));\n    public static final DeferredHolder<Block, Block> BLOCK_PROXY = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:proxy\"));\n    public static final DeferredHolder<Block, BlockSqueezer> BLOCK_SQUEEZER = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:squeezer\"));\n    public static final DeferredHolder<Block, Block> BLOCK_VARIABLE_STORE = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:variablestore\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_TORCH = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_torch\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_TORCH_WALL = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_torch_wall\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_TORCH_STONE = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_torch_stone\"));\n    public static final DeferredHolder<Block, Block> BLOCK_MENRIL_TORCH_STONE_WALL = DeferredHolder.create(Registries.BLOCK, Identifier.parse(\"integrateddynamics:menril_torch_stone_wall\"));\n\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityCoalGenerator>> BLOCK_ENTITY_COAL_GENERATOR = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:coal_generator\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityDelay>> BLOCK_ENTITY_DELAY = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:delay\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityDryingBasin>> BLOCK_ENTITY_DRYING_BASIN = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:drying_basin\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityEnergyBattery>> BLOCK_ENTITY_ENERGY_BATTERY = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:energy_battery\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityMaterializer>> BLOCK_ENTITY_MATERIALIZER = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:materializer\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityMechanicalDryingBasin>> BLOCK_ENTITY_MECHANICAL_DRYING_BASIN = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:mechanical_drying_basin\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityMechanicalSqueezer>> BLOCK_ENTITY_MECHANICAL_SQUEEZER = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:mechanical_squeezer\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityMultipartTicking>> BLOCK_ENTITY_MULTIPART_TICKING = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:multipart_ticking\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityProxy>> BLOCK_ENTITY_PROXY = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:proxy\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntitySqueezer>> BLOCK_ENTITY_SQUEEZER = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:squeezer\"));\n    public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BlockEntityVariablestore>> BLOCK_ENTITY_VARIABLE_STORE = DeferredHolder.create(Registries.BLOCK_ENTITY_TYPE, Identifier.parse(\"integrateddynamics:variable_store\"));\n\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerAspectSettings>> CONTAINER_ASPECT_SETTINGS = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:aspect_settings\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerCoalGenerator>> CONTAINER_COAL_GENERATOR = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:coal_generator\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerDelay>> CONTAINER_DELAY = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:delay\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerLabeller>> CONTAINER_LABELLER = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:labeller\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerLogicProgrammer>> CONTAINER_LOGIC_PROGRAMMER = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:logic_programmer\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerLogicProgrammerPortable>> CONTAINER_LOGIC_PROGRAMMER_PORTABLE = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:logic_programmer_portable\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerMaterializer>> CONTAINER_MATERIALIZER = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:materializer\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerMaterializer>> CONTAINER_MECHANICAL_DRYING_BASIN = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:mechanical_drying_basin\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerMaterializer>> CONTAINER_MECHANICAL_SQUEEZER = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:mechanical_squeezer\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerOnTheDynamicsOfIntegration>> CONTAINER_ON_THE_DYNAMICS_OF_INTEGRATION = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:on_the_dynamics_of_integration\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerPartPanelVariableDriven>> CONTAINER_PART_DISPLAY = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:part_display\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerPartReader>> CONTAINER_PART_READER = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:part_reader\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerPartSettings>> CONTAINER_PART_SETTINGS = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:part_settings\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerPartSettings>> CONTAINER_PART_OFFSET = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:part_offset\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerPartReader>> CONTAINER_PART_WRITER = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:part_writer\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerProxy>> CONTAINER_PROXY = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:proxy\"));\n    public static final DeferredHolder<MenuType<?>, MenuType<ContainerVariablestore>> CONTAINER_VARIABLESTORE = DeferredHolder.create(Registries.MENU, Identifier.parse(\"integrateddynamics:variablestore\"));\n\n    public static final DeferredHolder<Fluid, FlowingFluid> FLUID_LIQUID_CHORUS = DeferredHolder.create(Registries.FLUID, Identifier.parse(\"integrateddynamics:liquid_chorus\"));\n    public static final DeferredHolder<Fluid, FlowingFluid> FLUID_MENRIL_RESIN = DeferredHolder.create(Registries.FLUID, Identifier.parse(\"integrateddynamics:menril_resin\"));\n\n    public static final DeferredHolder<FoliagePlacerType<?>, FoliagePlacerType<FoliagePlacerMenril>> FOLIAGE_PLACER_MENRIL = DeferredHolder.create(Registries.FOLIAGE_PLACER_TYPE, Identifier.parse(\"integrateddynamics:menril\"));\n\n    public static final DeferredHolder<TrunkPlacerType<?>, TrunkPlacerType<TrunkPlacerMenril>> TRUNK_PLACER_MENRIL = DeferredHolder.create(Registries.TRUNK_PLACER_TYPE, Identifier.parse(\"integrateddynamics:menril\"));\n\n    public static final DeferredHolder<Biome, Biome> BIOME_MENEGLIN = DeferredHolder.create(Registries.BIOME, Identifier.parse(\"integrateddynamics:meneglin\"));\n\n    public static final DeferredHolder<RecipeType<?>, RecipeType<RecipeDryingBasin>> RECIPETYPE_DRYING_BASIN = DeferredHolder.create(Registries.RECIPE_TYPE, Identifier.parse(\"integrateddynamics:drying_basin\"));\n    public static final DeferredHolder<RecipeType<?>, RecipeType<RecipeMechanicalDryingBasin>> RECIPETYPE_MECHANICAL_DRYING_BASIN = DeferredHolder.create(Registries.RECIPE_TYPE, Identifier.parse(\"integrateddynamics:mechanical_drying_basin\"));\n    public static final DeferredHolder<RecipeType<?>, RecipeType<RecipeSqueezer>> RECIPETYPE_SQUEEZER = DeferredHolder.create(Registries.RECIPE_TYPE, Identifier.parse(\"integrateddynamics:squeezer\"));\n    public static final DeferredHolder<RecipeType<?>, RecipeType<RecipeMechanicalSqueezer>> RECIPETYPE_MECHANICAL_SQUEEZER = DeferredHolder.create(Registries.RECIPE_TYPE, Identifier.parse(\"integrateddynamics:mechanical_squeezer\"));\n\n    public static final DeferredHolder<RecipeBookCategory, RecipeBookCategory> RECIPEBOOKCATEGORY_DRYING_BASIN = DeferredHolder.create(Registries.RECIPE_BOOK_CATEGORY, Identifier.parse(\"integrateddynamics:drying_basin\"));\n    public static final DeferredHolder<RecipeBookCategory, RecipeBookCategory> RECIPEBOOKCATEGORY_MECHANICAL_DRYING_BASIN = DeferredHolder.create(Registries.RECIPE_BOOK_CATEGORY, Identifier.parse(\"integrateddynamics:mechanical_drying_basin\"));\n    public static final DeferredHolder<RecipeBookCategory, RecipeBookCategory> RECIPEBOOKCATEGORY_SQUEEZER = DeferredHolder.create(Registries.RECIPE_BOOK_CATEGORY, Identifier.parse(\"integrateddynamics:squeezer\"));\n    public static final DeferredHolder<RecipeBookCategory, RecipeBookCategory> RECIPEBOOKCATEGORY_MECHANICAL_SQUEEZER = DeferredHolder.create(Registries.RECIPE_BOOK_CATEGORY, Identifier.parse(\"integrateddynamics:mechanical_squeezer\"));\n\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeDryingBasin>> RECIPESERIALIZER_DRYING_BASIN = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:drying_basin\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeMechanicalDryingBasin>> RECIPESERIALIZER_MECHANICAL_DRYING_BASIN = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:mechanical_drying_basin\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeSqueezer>> RECIPESERIALIZER_SQUEEZER = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:squeezer\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeMechanicalSqueezer>> RECIPESERIALIZER_MECHANICAL_SQUEEZER = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:mechanical_squeezer\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeSqueezer>> RECIPESERIALIZER_SQUEEZER_FACADE = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:squeezer_facade\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeMechanicalSqueezer>> RECIPESERIALIZER_MECHANICAL_SQUEEZER_FACADE = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:mechanical_squeezer_facade\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeNbtClear>> RECIPESERIALIZER_NBT_CLEAR = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:crafting_special_nbt_clear\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<RecipeEnergyContainerCombination>> RECIPESERIALIZER_ENERGY_CONTAINER_COMBINATION = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:crafting_special_energycontainer_combination\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<ItemFacadeRecipe>> RECIPESERIALIZER_FACADE = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:crafting_special_facade\"));\n    public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<ItemVariableCopyRecipe>> RECIPESERIALIZER_VARIABLE_COPY = DeferredHolder.create(Registries.RECIPE_SERIALIZER, Identifier.parse(\"integrateddynamics:crafting_special_variable_copy\"));\n\n    public static final DeferredHolder<SoundEvent, SoundEvent> SOUNDEVENT_EFFECT_PAGE_FLIPSINGLE = DeferredHolder.create(Registries.SOUND_EVENT, Identifier.parse(\"integrateddynamics:effect_page_flipsingle\"));\n    public static final DeferredHolder<SoundEvent, SoundEvent> SOUNDEVENT_EFFECT_PAGE_FLIPMULTIPLE = DeferredHolder.create(Registries.SOUND_EVENT, Identifier.parse(\"integrateddynamics:effect_page_flipmultiple\"));\n\n    public static final DeferredHolder<EntityType<?>, EntityType<? extends EntityItemTargetted>> ENTITYTYPE_ITEM_TARGETTED = DeferredHolder.create(Registries.ENTITY_TYPE, Identifier.parse(\"integrateddynamics:item_targetted\"));\n\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<Boolean>> DATACOMPONENT_ACTIVATED = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:activated\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<BlockState>> DATACOMPONENT_FACADE_BLOCK = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:facade_block\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<Pair<SimpleFluidContent, SimpleFluidContent>>> DATACOMPONENT_FLUID_CONTENT_IN_OUT = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:fluid_content_in_out\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<Integer>> DATACOMPONENT_OMNIDIRECTIONAL_GROUP = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:omnidirectional_group\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<Integer>> DATACOMPONENT_PART_ENHANCEMENT = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:part_enhancement\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<CompoundTag>> DATACOMPONENT_PART_STATE = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:part_state\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<Integer>> DATACOMPONENT_PROXY_ID = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:proxy_id\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<CompoundTag>> DATACOMPONENT_VARIABLE_FACADE = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:variable_facade\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<BlockPos>> DATACOMPONENT_WRENCH_TARGET_BLOCKPOS = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:wrench_target_blockpos\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<Direction>> DATACOMPONENT_WRENCH_TARGET_DIRECTION = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:wrench_target_direction\"));\n    public static final DeferredHolder<DataComponentType<?>, DataComponentType<ItemWrench.Mode>> DATACOMPONENT_WRENCH_MODE = DeferredHolder.create(Registries.DATA_COMPONENT_TYPE, Identifier.parse(\"integrateddynamics:wrench_mode\"));\n\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/NetworkInitializedTrigger.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.advancements.criterion.ContextAwarePredicate;\nimport net.minecraft.advancements.criterion.EntityPredicate;\nimport net.minecraft.advancements.criterion.SimpleCriterionTrigger;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.advancement.criterion.ICriterionInstanceTestable;\nimport org.cyclops.integrateddynamics.core.network.event.NetworkInitializedEvent;\n\nimport java.util.Optional;\n\n/**\n * Triggers when a network is initialized.\n * @author rubensworks\n */\npublic class NetworkInitializedTrigger extends SimpleCriterionTrigger<NetworkInitializedTrigger.Instance> {\n\n    public static final Codec<NetworkInitializedTrigger.Instance> CODEC = RecordCodecBuilder.create(\n            p_311401_ -> p_311401_.group(\n                            EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf(\"player\").forGetter(NetworkInitializedTrigger.Instance::player),\n                            Codec.INT.optionalFieldOf(\"min_cables\").forGetter(NetworkInitializedTrigger.Instance::minCables)\n                    )\n                    .apply(p_311401_, NetworkInitializedTrigger.Instance::new)\n    );\n\n    public NetworkInitializedTrigger() {\n        NeoForge.EVENT_BUS.register(this);\n    }\n\n    @Override\n    public Codec<NetworkInitializedTrigger.Instance> codec() {\n        return CODEC;\n    }\n\n    public void test(ServerPlayer player, NetworkInitializedEvent event) {\n        this.trigger(player, (instance) -> instance.test(player, event));\n    }\n\n    @SubscribeEvent\n    public void onCrafted(NetworkInitializedEvent event) {\n        if (event.getPlacer() != null && event.getPlacer() instanceof ServerPlayer) {\n            this.test((ServerPlayer) event.getPlacer(), event);\n        }\n    }\n\n    public static record Instance(\n            Optional<ContextAwarePredicate> player,\n            Optional<Integer> minCables\n    ) implements SimpleCriterionTrigger.SimpleInstance, ICriterionInstanceTestable<NetworkInitializedEvent> {\n        @Override\n        public boolean test(ServerPlayer player, NetworkInitializedEvent networkEvent) {\n            return minCables\n                    .map(minCablesCount -> networkEvent.getNetwork().getCablesCount() >= minCablesCount)\n                    .orElse(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/NetworkInitializedTriggerConfig.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.CriterionTriggerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n *\n */\npublic class NetworkInitializedTriggerConfig extends CriterionTriggerConfigCommon<NetworkInitializedTrigger.Instance, IntegratedDynamics> {\n\n    public NetworkInitializedTriggerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"network_initialized\",\n                new NetworkInitializedTrigger()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/PartReaderAspectTrigger.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.advancements.criterion.ContextAwarePredicate;\nimport net.minecraft.advancements.criterion.EntityPredicate;\nimport net.minecraft.advancements.criterion.SimpleCriterionTrigger;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.advancement.criterion.ICriterionInstanceTestable;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.event.PartReaderAspectEvent;\n\nimport java.util.Optional;\n\n/**\n * Triggers when a part reader aspect is set.\n * @author rubensworks\n */\npublic class PartReaderAspectTrigger extends SimpleCriterionTrigger<PartReaderAspectTrigger.Instance> {\n\n    public static final Codec<PartReaderAspectTrigger.Instance> CODEC = RecordCodecBuilder.create(\n            p_311401_ -> p_311401_.group(\n                            EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf(\"player\").forGetter(PartReaderAspectTrigger.Instance::player),\n                            Codecs.PART_TYPE.optionalFieldOf(\"parttype\").forGetter(PartReaderAspectTrigger.Instance::partType),\n                            Codecs.ASPECT.optionalFieldOf(\"aspect\").forGetter(PartReaderAspectTrigger.Instance::aspect)\n                    )\n                    .apply(p_311401_, PartReaderAspectTrigger.Instance::new)\n    );\n\n    public PartReaderAspectTrigger() {\n        NeoForge.EVENT_BUS.register(this);\n    }\n\n    @Override\n    public Codec<Instance> codec() {\n        return CODEC;\n    }\n\n    public void test(ServerPlayer player, PartReaderAspectEvent event) {\n        this.trigger(player, (instance) -> instance.test(player, event));\n    }\n\n    @SubscribeEvent\n    public void onEvent(PartReaderAspectEvent event) {\n        if (event.getEntityPlayer() != null && event.getEntityPlayer() instanceof ServerPlayer) {\n            this.test((ServerPlayer) event.getEntityPlayer(), event);\n        }\n    }\n\n    public static record Instance(\n            Optional<ContextAwarePredicate> player,\n            Optional<IPartType> partType,\n            Optional<IAspect> aspect\n    ) implements SimpleCriterionTrigger.SimpleInstance, ICriterionInstanceTestable<PartReaderAspectEvent> {\n        @Override\n        public boolean test(ServerPlayer player, PartReaderAspectEvent event) {\n            return (partType.isEmpty() || event.getPartType() == partType.get())\n                    && (aspect.isEmpty() || event.getAspect() == aspect.get());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/PartReaderAspectTriggerConfig.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.CriterionTriggerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n *\n */\npublic class PartReaderAspectTriggerConfig extends CriterionTriggerConfigCommon<PartReaderAspectTrigger.Instance, IntegratedDynamics> {\n\n    public PartReaderAspectTriggerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"part_reader_aspect\",\n                new PartReaderAspectTrigger()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/PartVariableDrivenAspectTrigger.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.advancements.criterion.ContextAwarePredicate;\nimport net.minecraft.advancements.criterion.EntityPredicate;\nimport net.minecraft.advancements.criterion.SimpleCriterionTrigger;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.advancement.criterion.ICriterionInstanceTestable;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariablePredicate;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\nimport org.cyclops.integrateddynamics.core.part.event.PartVariableDrivenVariableContentsUpdatedEvent;\n\nimport java.util.Optional;\n\n/**\n * Triggers when a variable-driven part aspect is changed.\n * @author rubensworks\n */\npublic class PartVariableDrivenAspectTrigger extends SimpleCriterionTrigger<PartVariableDrivenAspectTrigger.Instance> {\n\n    public static final Codec<PartVariableDrivenAspectTrigger.Instance> CODEC = RecordCodecBuilder.create(\n            p_311401_ -> p_311401_.group(\n                            EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf(\"player\").forGetter(PartVariableDrivenAspectTrigger.Instance::player),\n                            Codecs.PART_TYPE.optionalFieldOf(\"part_type\").forGetter(PartVariableDrivenAspectTrigger.Instance::partType),\n                            Codecs.VARIABLE.optionalFieldOf(\"variable\").forGetter(PartVariableDrivenAspectTrigger.Instance::variablePredicate)\n                    )\n                    .apply(p_311401_, PartVariableDrivenAspectTrigger.Instance::new)\n    );\n\n    public PartVariableDrivenAspectTrigger() {\n        NeoForge.EVENT_BUS.register(this);\n    }\n\n    @Override\n    public Codec<Instance> codec() {\n        return CODEC;\n    }\n\n    public void test(ServerPlayer player, PartVariableDrivenVariableContentsUpdatedEvent event) {\n        this.trigger(player, (instance) -> instance.test(player, event));\n    }\n\n    @SubscribeEvent\n    public void onEvent(PartVariableDrivenVariableContentsUpdatedEvent event) {\n        if (event.getEntityPlayer() != null && event.getEntityPlayer() instanceof ServerPlayer) {\n            this.test((ServerPlayer) event.getEntityPlayer(), event);\n        }\n    }\n\n    public static record Instance(\n            Optional<ContextAwarePredicate> player,\n            Optional<IPartType> partType,\n            Optional<VariablePredicate> variablePredicate\n    ) implements SimpleCriterionTrigger.SimpleInstance, ICriterionInstanceTestable<PartVariableDrivenVariableContentsUpdatedEvent> {\n        @Override\n        public boolean test(ServerPlayer player, PartVariableDrivenVariableContentsUpdatedEvent event) {\n            return (partType.isEmpty() || event.getPartType() == partType.get())\n                    && variablePredicate.orElse(VariablePredicate.ANY).test(event.getVariable());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/PartVariableDrivenAspectTriggerConfig.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.CriterionTriggerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n *\n */\npublic class PartVariableDrivenAspectTriggerConfig extends CriterionTriggerConfigCommon<PartVariableDrivenAspectTrigger.Instance, IntegratedDynamics> {\n\n    public PartVariableDrivenAspectTriggerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"part_variable_driven\",\n                new PartVariableDrivenAspectTrigger()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/PartWriterAspectTrigger.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.advancements.criterion.ContextAwarePredicate;\nimport net.minecraft.advancements.criterion.EntityPredicate;\nimport net.minecraft.advancements.criterion.SimpleCriterionTrigger;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.advancement.criterion.ICriterionInstanceTestable;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariablePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\nimport org.cyclops.integrateddynamics.core.part.event.PartWriterAspectEvent;\n\nimport java.util.Optional;\n\n/**\n * Triggers when a part writer aspect is set.\n * @author rubensworks\n */\npublic class PartWriterAspectTrigger extends SimpleCriterionTrigger<PartWriterAspectTrigger.Instance> {\n\n    public static final Codec<PartWriterAspectTrigger.Instance> CODEC = RecordCodecBuilder.create(\n            p_311401_ -> p_311401_.group(\n                            EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf(\"player\").forGetter(PartWriterAspectTrigger.Instance::player),\n                            Codecs.PART_TYPE.optionalFieldOf(\"parttype\").forGetter(PartWriterAspectTrigger.Instance::partType),\n                            Codecs.ASPECT.optionalFieldOf(\"aspect\").forGetter(PartWriterAspectTrigger.Instance::aspect),\n                            Codecs.VARIABLE.optionalFieldOf(\"variable\").forGetter(PartWriterAspectTrigger.Instance::variablePredicate)\n                    )\n                    .apply(p_311401_, PartWriterAspectTrigger.Instance::new)\n    );\n\n    public PartWriterAspectTrigger() {\n        NeoForge.EVENT_BUS.register(this);\n    }\n\n    @Override\n    public Codec<Instance> codec() {\n        return CODEC;\n    }\n\n    public void test(ServerPlayer player, PartWriterAspectEvent event) {\n        this.trigger(player, (instance) -> instance.test(player, event));\n    }\n\n    @SubscribeEvent\n    public void onEvent(PartWriterAspectEvent event) {\n        if (event.getEntityPlayer() != null && event.getEntityPlayer() instanceof ServerPlayer) {\n            this.test((ServerPlayer) event.getEntityPlayer(), event);\n        }\n    }\n\n    public static record Instance(\n            Optional<ContextAwarePredicate> player,\n            Optional<IPartType> partType,\n            Optional<IAspect> aspect,\n            Optional<VariablePredicate> variablePredicate\n    ) implements SimpleCriterionTrigger.SimpleInstance, ICriterionInstanceTestable<PartWriterAspectEvent> {\n        @Override\n        public boolean test(ServerPlayer player, PartWriterAspectEvent event) {\n            return (partType.isEmpty() || event.getPartType() == partType.get())\n                    && (aspect.isEmpty() || event.getAspect() == aspect.get())\n                    && variablePredicate.orElse(VariablePredicate.ANY).test(((IPartStateWriter) event.getPartState()).getVariable(event.getNetwork(), event.getPartNetwork(), ValueDeseralizationContext.of(player.level())));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/PartWriterAspectTriggerConfig.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.CriterionTriggerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n *\n */\npublic class PartWriterAspectTriggerConfig extends CriterionTriggerConfigCommon<PartWriterAspectTrigger.Instance, IntegratedDynamics> {\n\n    public PartWriterAspectTriggerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"part_writer_aspect\",\n                new PartWriterAspectTrigger()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/VariableCreatedTrigger.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.advancements.criterion.ContextAwarePredicate;\nimport net.minecraft.advancements.criterion.EntityPredicate;\nimport net.minecraft.advancements.criterion.SimpleCriterionTrigger;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.level.block.Block;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.advancement.criterion.ICriterionInstanceTestable;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariableFacadePredicate;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.event.LogicProgrammerVariableFacadeCreatedEvent;\n\nimport java.util.Optional;\n\n/**\n * Triggers when a variable is created.\n * @author rubensworks\n */\npublic class VariableCreatedTrigger extends SimpleCriterionTrigger<VariableCreatedTrigger.Instance> {\n\n    public static final Codec<VariableCreatedTrigger.Instance> CODEC = RecordCodecBuilder.create(\n            p_311401_ -> p_311401_.group(\n                            EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf(\"player\").forGetter(VariableCreatedTrigger.Instance::player),\n                            BuiltInRegistries.BLOCK.byNameCodec().optionalFieldOf(\"block\").forGetter(VariableCreatedTrigger.Instance::block),\n                            Codecs.VARIABLE_FACADE.optionalFieldOf(\"variable_facade\").forGetter(VariableCreatedTrigger.Instance::variableFacadePredicate)\n                    )\n                    .apply(p_311401_, VariableCreatedTrigger.Instance::new)\n    );\n\n    public VariableCreatedTrigger() {\n        NeoForge.EVENT_BUS.register(this);\n    }\n\n    @Override\n    public Codec<Instance> codec() {\n        return CODEC;\n    }\n\n    public void test(ServerPlayer player, LogicProgrammerVariableFacadeCreatedEvent event) {\n        this.trigger(player, (instance) -> instance.test(player, event));\n    }\n\n    @SubscribeEvent\n    public void onEvent(LogicProgrammerVariableFacadeCreatedEvent event) {\n        if (event.getPlayer() != null && event.getPlayer() instanceof ServerPlayer) {\n            this.test((ServerPlayer) event.getPlayer(), event);\n        }\n    }\n\n    public static record Instance(\n            Optional<ContextAwarePredicate> player,\n            Optional<Block> block,\n            Optional<VariableFacadePredicate> variableFacadePredicate\n    ) implements SimpleCriterionTrigger.SimpleInstance, ICriterionInstanceTestable<LogicProgrammerVariableFacadeCreatedEvent> {\n        @Override\n        public boolean test(ServerPlayer player, LogicProgrammerVariableFacadeCreatedEvent event) {\n            return (block.isEmpty() || event.getBlockState().getBlock() == block.get())\n                    && variableFacadePredicate.orElse(VariableFacadePredicate.ANY).test(event.getVariableFacade());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/advancement/criterion/VariableCreatedTriggerConfig.java",
    "content": "package org.cyclops.integrateddynamics.advancement.criterion;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.CriterionTriggerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n *\n */\npublic class VariableCreatedTriggerConfig extends CriterionTriggerConfigCommon<VariableCreatedTrigger.Instance, IntegratedDynamics> {\n\n    public VariableCreatedTriggerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"variable_created\",\n                new VariableCreatedTrigger()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/APIReference.java",
    "content": "package org.cyclops.integrateddynamics.api;\n\n/**\n * Reference values for the API.\n * @author rubensworks\n */\npublic class APIReference {\n\n    public static final String API_VERSION = \"@API_VERSION@\";\n    public static final String API_OWNER = \"integrateddynamics\";\n    public static final String API_PROVIDES = \"integrateddynamics|api\";\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/IntegratedDynamicsAPI.java",
    "content": "package org.cyclops.integrateddynamics.api;\n\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.init.RegistryManager;\n\n/**\n * Main API class for Integrated Dynamics.\n * @author rubensworks\n */\npublic class IntegratedDynamicsAPI {\n\n    private static ModBaseNeoForge mod;\n\n    static {\n        try {\n            Class<?> mainClass = Class.forName(\"org.cyclops.integrateddynamics.IntegratedDynamics\");\n            mod = (ModBaseNeoForge) mainClass.getField(\"_instance\").get(null);\n        } catch (ClassNotFoundException | NoSuchFieldException | ClassCastException | IllegalAccessException e) {\n            //e.printStackTrace();\n        }\n    }\n\n    /**\n     * @return If the Integrated Dynamics mod is loaded.\n     */\n    public static boolean isPresent() {\n        return mod != null;\n    }\n\n    /**\n     * @return The registry manager.\n     */\n    public static RegistryManager getRegistryManager() {\n        return mod.getRegistryManager();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/PartStateException.java",
    "content": "package org.cyclops.integrateddynamics.api;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\n/**\n * A runtime exception that can be thrown when a part is in an invalid state.\n * @author rubensworks\n */\npublic class PartStateException extends IllegalArgumentException {\n\n    public PartStateException(DimPos dimPos, Direction side) {\n        super(String.format(\"No part state for part at position %s side %s was found.\" +\n                        \"\\nWorld loaded: %s\\nChunk loaded: %s\\nPart container: %s\\nParts: %s\",\n                dimPos,\n                side,\n                dimPos.getLevel(false) != null,\n                dimPos.isLoaded(),\n                dimPos.isLoaded() ? PartHelpers.getPartContainer(dimPos, side).orElse(null) : null,\n                dimPos.isLoaded() ? PartHelpers.getPartContainer(dimPos, side).map(IPartContainer::getParts).orElse(null) : null));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/advancement/criterion/ValuePredicate.java",
    "content": "package org.cyclops.integrateddynamics.api.advancement.criterion;\n\nimport com.google.gson.JsonElement;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\n\nimport java.util.Optional;\n\n/**\n * A predicate for values of a certain type.\n * @author rubensworks\n */\npublic class ValuePredicate<V extends IValue> {\n\n    public static final ValuePredicate ANY = new ValuePredicate<>(Optional.empty(), Optional.empty(), Optional.empty());\n\n    private final Optional<IValueType> valueType;\n    private final Optional<IValue> value;\n    private final Optional<JsonElement> valueJson;\n\n    public ValuePredicate(Optional<IValueType> valueType, Optional<IValue> value, Optional<JsonElement> valueJson) {\n        this.valueType = valueType;\n        this.value = value;\n        this.valueJson = valueJson;\n    }\n\n    public Optional<IValueType> getValueType() {\n        return valueType;\n    }\n\n    public Optional<IValue> getValue() {\n        return value;\n    }\n\n    public Optional<JsonElement> getValueJson() {\n        return valueJson;\n    }\n\n    public final boolean test(IValue value) {\n        return (this.value.isEmpty() || ValueHelpers.areValuesEqual(this.value.get(), value))\n                && (this.valueType.isEmpty() || value.getType() == this.valueType.get()) && testTyped((V) value);\n    }\n\n    protected boolean testTyped(V value) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/advancement/criterion/VariableFacadePredicate.java",
    "content": "package org.cyclops.integrateddynamics.api.advancement.criterion;\n\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\n\n/**\n * A predicate for variable facades of all types.\n * @author rubensworks\n */\npublic class VariableFacadePredicate<V extends IVariableFacade> {\n\n    public static final VariableFacadePredicate ANY = new VariableFacadePredicate<>(IVariableFacade.class);\n\n    private final Class<V> variableClass;\n\n    public VariableFacadePredicate(Class<V> variableClass) {\n        this.variableClass = variableClass;\n    }\n\n    public final boolean test(IVariableFacade variableFacade) {\n        return variableClass.isInstance(variableFacade) && testTyped((V) variableFacade);\n    }\n\n    protected boolean testTyped(V variableFacade) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/advancement/criterion/VariablePredicate.java",
    "content": "package org.cyclops.integrateddynamics.api.advancement.criterion;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\n\nimport java.util.Optional;\n\n/**\n * A predicate for variables of all types.\n * @author rubensworks\n */\npublic class VariablePredicate<V extends IVariable> {\n\n    public static final VariablePredicate ANY = new VariablePredicate<>(IVariable.class, Optional.empty(), Optional.empty());\n\n    private final Class<V> variableClass;\n    private final Optional<IValueType> valueType;\n    private final Optional<ValuePredicate> valuePredicate;\n\n    public VariablePredicate(Class<V> variableClass, Optional<IValueType> valueType, Optional<ValuePredicate> valuePredicate) {\n        this.variableClass = variableClass;\n        this.valueType = valueType;\n        this.valuePredicate = valuePredicate;\n    }\n\n    public Optional<IValueType> getValueType() {\n        return valueType;\n    }\n\n    public Optional<ValuePredicate> getValuePredicate() {\n        return valuePredicate;\n    }\n\n    public final boolean test(IVariable variable) {\n        try {\n            return variableClass.isInstance(variable)\n                    && (this.valueType.isEmpty() || ValueHelpers.correspondsTo(this.valueType.get(), variable.getType()))\n                    && (this.valuePredicate.isEmpty() || this.valuePredicate.get().test(variable.getValue()))\n                    && testTyped((V) variable);\n        } catch (EvaluationException e) {\n            return false;\n        }\n    }\n\n    protected boolean testTyped(V variable) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/block/IDynamicLight.java",
    "content": "package org.cyclops.integrateddynamics.api.block;\n\n/**\n * Capability that can have its light level updated and stored.\n * @author rubensworks\n */\npublic interface IDynamicLight {\n\n    /**\n     * Set the light level.\n     * @param level The light level.\n     */\n    public void setLightLevel(int level);\n\n    /**\n     * Get the light level.\n     * @return The light level.\n     */\n    public int getLightLevel();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/block/IDynamicRedstone.java",
    "content": "package org.cyclops.integrateddynamics.api.block;\n\n/**\n * Capability that can have its redstone level updated and stored.\n * @author rubensworks\n */\npublic interface IDynamicRedstone {\n\n    /**\n     * Set the redstone level.\n     * @param level The redstone level.\n     * @param direct If the redstone power should be direct.\n     */\n    public void setRedstoneLevel(int level, boolean direct);\n\n    /**\n     * Get the redstone level.\n     * @return The redstone level.\n     */\n    public int getRedstoneLevel();\n\n    /**\n     * @return If the redstone power is direct.\n     */\n    public boolean isDirect();\n\n    /**\n     * Set if this side allows redstone to be inputted.\n     * @param allow If it allows input.\n     */\n    public void setAllowRedstoneInput(boolean allow);\n\n    /**\n     * If this side allows redstone to be inputted.\n     * @return If it allows input.\n     */\n    public boolean isAllowRedstoneInput();\n\n    /**\n     * Store the last value that was used to trigger a redstone pulse.\n     * @param value A pulse value.\n     */\n    public void setLastPulseValue(int value);\n\n    /**\n     * @return The last pulse value.\n     */\n    public int getLastPulseValue();\n\n    /**\n     * Set the remaining tick duration for an active pulse.\n     * @param ticks The number of ticks remaining, or 0 if no pulse is active.\n     */\n    public void setScheduledPulseRemaining(int ticks);\n\n    /**\n     * @return The number of ticks remaining for an active pulse, or 0 if no pulse is active.\n     */\n    public int getScheduledPulseRemaining();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/block/IFacadeable.java",
    "content": "package org.cyclops.integrateddynamics.api.block;\n\nimport net.minecraft.world.level.block.state.BlockState;\n\nimport javax.annotation.Nullable;\n\n/**\n * Capability for targets that can hold facades.\n * @author rubensworks\n */\npublic interface IFacadeable {\n\n    /**\n     * @return If this container has a facade.\n     */\n    public boolean hasFacade();\n\n    /**\n     * @return The blockstate of the facade.\n     */\n    public BlockState getFacade();\n\n    /**\n     * Set the new facade\n     * @param blockState The new facade or null.\n     */\n    public void setFacade(@Nullable BlockState blockState);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/block/IVariableContainer.java",
    "content": "package org.cyclops.integrateddynamics.api.block;\n\nimport net.minecraft.world.Container;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\n\nimport java.util.Map;\n\n/**\n * Capability that can hold {@link IVariableFacade}s.\n * @author rubensworks\n */\npublic interface IVariableContainer {\n\n    /**\n     * @return The stored variable facades for this part.\n     */\n    public Map<Integer, IVariableFacade> getVariableCache();\n\n    /**\n     * Invalidate variables in this cache, clear the cache and re-populate from the supplied inventory\n     * @param network {@link INetwork} that the variables are in\n     * @param inventory IInventory to re-populate the cache from\n     * @param sendVariablesUpdateEvent if true post a VariableContentsUpdatedEvent to the network when done\n     * @param valueDeseralizationContext\n     */\n    public void refreshVariables(INetwork network, Container inventory, boolean sendVariablesUpdateEvent, ValueDeseralizationContext valueDeseralizationContext);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/block/cable/ICable.java",
    "content": "package org.cyclops.integrateddynamics.api.block.cable;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\n\n/**\n * Capability for cables that can form networks.\n * Note that this is an UNSIDED capability, so only one should exist per side.\n * This is because cable sides are too dependent of each other.\n * @author rubensworks\n */\npublic interface ICable {\n\n    /**\n     * Check if this part should connect with the given cable for the given side.\n     * This method MUST NOT call the {@link ICable#canConnect(ICable, Direction)} method\n     * of the connector, this is checked externally, otherwise infinite loops will occur.\n     * @param connector The connecting block.\n     * @param side The side of the connecting block.\n     * @return If it should connect.\n     */\n    public boolean canConnect(ICable connector, Direction side);\n\n    public default void updateConnections() {\n        this.updateConnections(true);\n    }\n\n    /**\n     * Update the cable connections.\n     * @param setChanged If the block entity should be marked as changed.\n     */\n    public void updateConnections(boolean setChanged);\n\n    /**\n     * Check if this cable is connected to a side.\n     * This method should not check any neighbours,\n     * it should internally store the connection.\n     * @param side The side to check a connection for.\n     * @return If this block is connected with that side.\n     */\n    public boolean isConnected(Direction side);\n\n    /**\n     * Disconnect the cable connection for a side.\n     * @param side The side to block the connection for.\n     */\n    public void disconnect(Direction side);\n\n    /**\n     * Reconnect the cable connection for a side.\n     * Will only do something if the cable was previously disconnected.\n     * @param side The side to remake the connection for.\n     */\n    public void reconnect(Direction side);\n\n    /**\n     * @return The item stack that is dropped when breaking this cable, when using a wrench for example.\n     */\n    public ItemStack getItemStack();\n\n    /**\n     * Called when this cable is removed by a wrench.\n     */\n    public void destroy();\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/block/cable/ICableFakeable.java",
    "content": "package org.cyclops.integrateddynamics.api.block.cable;\n\n/**\n * Capability for cables that can become unreal.\n * A cable can only become fake for a full block, not just for one side.\n * This means that for example parts can exist in that block space without the cable being there.\n * @author rubensworks\n */\npublic interface ICableFakeable {\n\n    /**\n     * @return If this cable is a real cable, otherwise it is just a holder block for parts without connections.\n     */\n    public boolean isRealCable();\n\n    /**\n     * @param real If this cable is a real cable, otherwise it is just a holder block for parts without connections.\n     */\n    public void setRealCable(boolean real);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/gui/subgui/IGuiInputElement.java",
    "content": "package org.cyclops.integrateddynamics.api.client.gui.subgui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\n\nimport java.util.function.Consumer;\n\n/**\n * An element inside the logic programmer.\n * @param <G> The type of gui.\n * @param <C> The type of container.\n * @param <S> The sub gui box type.\n * @author rubensworks\n */\npublic interface IGuiInputElement<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu, GIC extends IGuiInputElementClient<S, G, C>> {\n\n    public GIC getClient();\n\n    /**\n     * @return Name used for rendering.\n     */\n    public Component getName();\n\n    /**\n     * @param tooltipAdder The list to add tooltip lines to.\n     */\n    public void loadTooltip(Consumer<Component> tooltipAdder);\n\n    /**\n     * @return The render pattern.\n     */\n    public IConfigRenderPattern getRenderPattern();\n\n    /**\n     * Called when this element is activated.\n     */\n    public void activate();\n\n    /**\n     * Called when this element is deactivated.\n     */\n    public void deactivate();\n\n    /**\n     * Validates the current state of the element.\n     * @return An error or null.\n     */\n    public Component validate();\n\n    /**\n     * @return The color used to identify this element.\n     */\n    public int getColor();\n\n    /**\n     * @return The symbol used to identify this element.\n     */\n    public String getSymbol();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/gui/subgui/IGuiInputElementClient.java",
    "content": "package org.cyclops.integrateddynamics.api.client.gui.subgui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\n\n/**\n * @author rubensworks\n */\npublic interface IGuiInputElementClient<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu> {\n\n    /**\n     * @param baseX Base x\n     * @param baseY Base y\n     * @param maxWidth Max width\n     * @param maxHeight Max height\n     * @param gui The parent gui\n     * @param container The parent container\n     * @return A subgui that is shown when activated.\n     */\n    public S createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                          G gui, C container);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/gui/subgui/IGuiInputElementValueType.java",
    "content": "package org.cyclops.integrateddynamics.api.client.gui.subgui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\n\nimport java.util.function.Predicate;\n\n/**\n * A value type element inside the logic programmer.\n * @param <G> The type of gui.\n * @param <C> The type of container.\n * @param <S> The sub gui box type.\n * @author rubensworks\n */\npublic interface IGuiInputElementValueType<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu, GIC extends IGuiInputElementValueTypeClient<S, G, C>>\n        extends IGuiInputElement<S, G, C, GIC> {\n\n    public void setValidator(Predicate<IValue> validator);\n\n    public IValue getValue();\n\n    public void setValue(IValue value);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/gui/subgui/IGuiInputElementValueTypeClient.java",
    "content": "package org.cyclops.integrateddynamics.api.client.gui.subgui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\n\n/**\n * @author rubensworks\n */\npublic interface IGuiInputElementValueTypeClient<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu> extends IGuiInputElementClient<S, G, C> {\n\n    /**\n     * Set the currently stored value in the given sub gui.\n     * This is useful when the gui is reused for multiple elements where the actual value is stored in this element.\n     * @param subGui The sub gui to put the currently stored value in.\n     * @param sendToServer If the value must be sent to the server.\n     */\n    public void setValueInGui(S subGui, boolean sendToServer);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/gui/subgui/ISubGui.java",
    "content": "package org.cyclops.integrateddynamics.api.client.gui.subgui;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\n\nimport java.util.Comparator;\n\n/**\n * A gui part that can be rendered withing another gui.\n * @author rubensworks\n */\npublic interface ISubGui {\n\n    public void init(int guiLeft, int guiTop);\n\n    public void tick();\n\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY);\n\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY);\n\n    /**\n     * Char type event\n     * @param evt The character event\n     * @return True if all next actions should be skipped\n     */\n    public boolean charTyped(CharacterEvent evt);\n\n    /**\n     * Key press event\n     * @param evt The key event\n     * @return True if all next actions should be skipped\n     */\n    public boolean keyPressed(KeyEvent evt);\n\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick);\n\n    public static class SubGuiComparator implements Comparator<ISubGui> {\n\n        @Override\n        public int compare(ISubGui o1, ISubGui o2) {\n            return Integer.compare(o1.hashCode(), o2.hashCode());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/gui/subgui/ISubGuiBox.java",
    "content": "package org.cyclops.integrateddynamics.api.client.gui.subgui;\n\n/**\n * A subgui in a box shape.\n * @author rubensworks\n */\npublic interface ISubGuiBox extends ISubGui {\n\n    public int getX();\n    public int getY();\n    public int getWidth();\n    public int getHeight();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/model/IVariableModelBaked.java",
    "content": "package org.cyclops.integrateddynamics.api.client.model;\n\n/**\n * A facadeModel for variable items.\n * @author rubensworks\n */\npublic interface IVariableModelBaked {\n\n    /**\n     * Set the baked submodels for the given provider.\n     * @param provider The provider.\n     * @param subModels The baked sub facadeModel holder.\n     * @param <B> The baked sub facadeModel type.\n     */\n    public <B extends IVariableModelProvider.BakedModelProvider> void setSubModels(IVariableModelProvider<B> provider, B subModels);\n\n    /**\n     * Get the baked submodels for the given provider.\n     * @param provider The provider.\n     * @param <B> The baked sub facadeModel type.\n     * @return The baked facadeModel provider.\n     */\n    public <B extends IVariableModelProvider.BakedModelProvider> B getSubModels(IVariableModelProvider<B> provider);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/model/IVariableModelProvider.java",
    "content": "package org.cyclops.integrateddynamics.api.client.model;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.client.resources.model.ResolvableModel;\nimport org.joml.Matrix4fc;\n\n/**\n * A provider of variable overlay models.\n * @param <B> The type of baked facadeModel provider.\n * @author rubensworks\n */\npublic interface IVariableModelProvider<B extends IVariableModelProvider.BakedModelProvider> {\n\n    /**\n     * Load the models for this provider.\n     *\n     * @param bakingContext The facadeModel baker.\n     * @param matrix\n     * @return The baked moderl provider.\n     */\n    public B bakeOverlayModels(ItemModel.BakingContext bakingContext, Matrix4fc matrix);\n\n    /**\n     * Load all required models for this facadeModel provider into the given facadeModel loader.\n     *\n     * @param resolver The list of sub models that can be appended to, which will be registered afterwards.\n     */\n    void resolveDependencies(ResolvableModel.Resolver resolver);\n\n    /**\n     * Provider for baked models.\n     */\n    public static interface BakedModelProvider {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/model/IVariableModelProviderRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.client.model;\n\nimport org.cyclops.cyclopscore.init.IRegistry;\n\nimport java.util.List;\n\n/**\n * Registry for {@link IVariableModelProvider}.\n * @author rubensworks\n */\npublic interface IVariableModelProviderRegistry extends IRegistry {\n\n    /**\n     * Register a new provider.\n     * @param provider The provider to register.\n     * @param <E> The type of provider.\n     * @param <B> The type of the baked facadeModel provider.\n     * @return The registered provider\n     */\n    public <E extends IVariableModelProvider<B>, B extends IVariableModelProvider.BakedModelProvider> E addProvider(E provider);\n\n    /**\n     * @return All registered provider.\n     */\n    public List<IVariableModelProvider<? extends IVariableModelProvider.BakedModelProvider>> getProviders();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/render/part/IPartOverlayRenderer.java",
    "content": "package org.cyclops.integrateddynamics.api.client.render.part;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\n/**\n * A renderer used to render additional elements for a part.\n * @author rubensworks\n */\npublic interface IPartOverlayRenderer {\n\n    /**\n     * Render the overlay.\n     *\n     * @param context             The render context\n     * @param partContainer       The part container for this part\n     * @param direction           The direction this part is facing\n     * @param partType            The part type that is being overlayed\n     * @param partialTicks        The partial tick\n     * @param matrixStack         The matrix render stack.\n     * @param submitNodeCollector The render type buffer.\n     * @param combinedLight       The combined light value.\n     * @param combinedOverlay     The combined overlay value.\n     */\n    public void submitPartOverlay(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                                  Direction direction, IPartType partType, float partialTicks, PoseStack matrixStack,\n                                  SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/render/part/IPartOverlayRendererRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.client.render.part;\n\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\nimport java.util.Collection;\n\n/**\n * Registry for {@link IPartOverlayRenderer}.\n * @author rubensworks\n */\npublic interface IPartOverlayRendererRegistry extends IRegistry {\n\n    /**\n     * Register a new renderer for the given part type.\n     * Multiple renderers can be added per part type.\n     * @param partType The part type\n     * @param renderer The renderer.\n     * @param <R> The renderer type.\n     * @return The registered renderer.\n     */\n    public <R extends IPartOverlayRenderer> R register(IPartType<?, ?> partType, R renderer);\n\n    /**\n     * Get all part overlay renderers for the given part type.\n     * @param partType The part type\n     * @return All registered renderers.\n     */\n    public Collection<IPartOverlayRenderer> getRenderers(IPartType<?, ?> partType);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/render/valuetype/IValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.api.client.render.valuetype;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\n/**\n * Renderer used to display values for a given value type in the world.\n * @author rubensworks\n */\npublic interface IValueTypeWorldRenderer {\n\n    /**\n     * Render the overlay.\n     *\n     * @param context         The render context\n     * @param partContainer   The part container for this part\n     * @param direction       The direction this part is facing\n     * @param partType        The part type that is being overlayed\n     * @param value           The value to render\n     * @param partialTicks    The partial tick\n     * @param matrixStack     The matrix render stack.\n     * @param nodeCollector   The render type buffer.\n     * @param combinedLight   The combined light value.\n     * @param combinedOverlay The combined overlay value.\n     * @param alpha           The alpha to render with.\n     */\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/client/render/valuetype/IValueTypeWorldRendererRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.client.render.valuetype;\n\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport javax.annotation.Nullable;\n\n/**\n * Registry for {@link IValueTypeWorldRenderer}.\n * @author rubensworks\n */\npublic interface IValueTypeWorldRendererRegistry extends IRegistry {\n\n    /**\n     * Set the renderer for the given value type.\n     * @param valueType The value type\n     * @param renderer The renderer.\n     * @param <R> The renderer type.\n     * @return The registered renderer.\n     */\n    public <R extends IValueTypeWorldRenderer> R register(IValueType<?> valueType, R renderer);\n\n    /**\n     * Get the renderer for the value type.\n     * @param valueType The value type\n     * @return The registered renderer of null.\n     */\n    @Nullable\n    public IValueTypeWorldRenderer getRenderer(IValueType<?> valueType);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/EvaluationException.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.network.chat.MutableComponent;\n\nimport java.util.List;\n\n/**\n * Exception to indicate a failed evaluation.\n * @author rubensworks\n */\npublic class EvaluationException extends Exception {\n\n    private final MutableComponent errorMessage;\n    private boolean retryEvaluation;\n    private final List<Runnable> resolutionListeners;\n\n    public EvaluationException(MutableComponent errorMessage) {\n        super(errorMessage.toString());\n        this.errorMessage = errorMessage;\n        this.retryEvaluation = false;\n        this.resolutionListeners = Lists.newArrayList();\n    }\n\n    public MutableComponent getErrorMessage() {\n        return errorMessage;\n    }\n\n    /**\n     * This should only be set at construction time of this exception.\n     * @param retryEvaluation If the evaluation may be retried again in the next tick.\n     */\n    public void setRetryEvaluation(boolean retryEvaluation) {\n        this.retryEvaluation = retryEvaluation;\n    }\n\n    /**\n     * @return If the evaluation may be retried again in the next tick.\n     */\n    public boolean isRetryEvaluation() {\n        return retryEvaluation;\n    }\n\n    public void addResolutionListeners(Runnable listener) {\n        this.resolutionListeners.add(listener);\n    }\n\n    /**\n     * If evaluators halt operation due to this thrown evaluation,\n     * invoking this method will cause them to remove the exception and resume operation.\n     *\n     * In contrast to {@link #retryEvaluation}, this may be invoked anywhere within the lifetime of evaluation\n     * exceptions.\n     */\n    public void resolve() {\n        for (Runnable resolutionListener : Lists.newArrayList(this.resolutionListeners.listIterator())) {\n            resolutionListener.run();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/IValueInterface.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\n\nimport java.util.Optional;\n\n/**\n * A capability that can expose values.\n * @author rubensworks\n */\npublic interface IValueInterface {\n    /**\n     * Get a value.\n     *\n     * @return A value.\n     * @throws EvaluationException If an error occurs while constructing or evaluating the value.\n     */\n    public Optional<IValue> getValue() throws EvaluationException;\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/InvalidValueTypeException.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate;\n\nimport net.minecraft.network.chat.MutableComponent;\n\n/**\n * An exception to signal mismatching variable types.\n * @author rubensworks\n */\npublic class InvalidValueTypeException extends EvaluationException {\n\n    public InvalidValueTypeException(MutableComponent errorMessage) {\n        super(errorMessage);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/expression/IExpression.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.expression;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\n\n/**\n * Generic expression that can evaluate expressions with variables to a value.\n * @author rubensworks\n */\npublic interface IExpression<V extends IValue> extends IVariable<V> {\n\n    /**\n     * @return The current evaluation result of the input variables.\n     * @throws EvaluationException When something went wrong while evaluating.\n     */\n    public IValue evaluate() throws EvaluationException;\n\n    /**\n     * @return If this expression last evaluation resulted in an error.\n     */\n    public boolean hasErrored();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/expression/ILazyExpressionValueCache.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.expression;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\n\n/**\n * Cache object that is responsible for storing values of this expression.\n * This cache object is responsible for determining when the values need to be ejected from the cache.\n * @author rubensworks\n */\npublic interface ILazyExpressionValueCache {\n\n    public void setValue(int id, IValue value);\n    public boolean hasValue(int id);\n    public IValue getValue(int id);\n    public void removeValue(int id);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/expression/VariableAdapter.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.expression;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariableInvalidateListener;\n\nimport java.util.Set;\n\n/**\n * A basic variable implementation.\n * @author rubensworks\n */\npublic abstract class VariableAdapter<V extends IValue> implements IVariable<V> {\n\n    private Set<IVariableInvalidateListener> invalidateListeners = Sets.newIdentityHashSet();\n\n    @Override\n    public void invalidate() {\n        for (IVariableInvalidateListener invalidateListener : Lists.newArrayList(invalidateListeners)) {\n            invalidateListener.invalidate();\n        }\n        invalidateListeners.clear();\n    }\n\n    @Override\n    public void addInvalidationListener(IVariableInvalidateListener invalidateListener) {\n        invalidateListeners.add(invalidateListener);\n    }\n\n    @Override\n    public void removeInvalidationListener(IVariableInvalidateListener invalidateListener) {\n        invalidateListeners.remove(invalidateListener);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/operator/IOperator.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Consumer;\n\n/**\n * Relation on value types.\n * @author rubensworks\n */\npublic interface IOperator {\n\n    /**\n     * @return The unique name of this operator that will also be used for display.\n     */\n    public String getSymbol();\n\n    /**\n     * @return The unique name for this operator, only used for internal storage.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * @return The unique interact name for this operator, when interacting with this operator using external contexts,\n     *         such as Integrated Scripting.\n     */\n    public String getInteractName();\n\n    /**\n     * @return Override for the default global interact name prefix.\n     */\n    @Nullable\n    public String getGlobalInteractNamePrefix();\n\n    /**\n     * @return If the global interact name prefix should also be used for local scopes.\n     */\n    public boolean shouldAlsoPrefixLocalScope();\n\n    /**\n     * @return The global interact name.\n     */\n    public default String getGlobalInteractName() {\n        String globalInteractNamePrefix = this.getGlobalInteractNamePrefix() != null ?\n                this.getGlobalInteractNamePrefix() :\n                (this.getInputTypes().length > 0 ? this.getInputTypes()[0].getTypeName() : null);\n        return globalInteractNamePrefix != null ?\n                globalInteractNamePrefix + Helpers.capitalizeString(this.getInteractName()) :\n                this.getInteractName();\n    }\n\n    /**\n     * @return The scoped interact name.\n     */\n    public default String getScopedInteractName() {\n        return this.shouldAlsoPrefixLocalScope() ?\n                this.getGlobalInteractNamePrefix() + Helpers.capitalizeString(this.getInteractName()) :\n                this.getInteractName();\n    }\n\n    /**\n     * @return The unique unlocalized name for this operator.\n     */\n    public String getTranslationKey();\n\n    /**\n     * @return The unique unlocalized category name for this operator.\n     */\n    public String getUnlocalizedCategoryName();\n\n    /**\n     * @return The localized full name for this operator, includes category name\n     */\n    public MutableComponent getLocalizedNameFull();\n\n    /**\n     * Add tooltip lines for this aspect when hovered in a gui.\n     *\n     * @param tooltipAdder       The list to add lines to.\n     * @param appendOptionalInfo If shift-to-show info should be added.\n     */\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo);\n\n    /**\n     * @return The ordered types of values that are used as input for this operator.\n     */\n    public IValueType[] getInputTypes();\n\n    /**\n     * @return The type of value that is achieved when this operator is executed.\n     */\n    public IValueType getOutputType();\n\n    /**\n     * Get the output value type depending on the active input of the operator.\n     * @param input The input that would be given during evaluation.\n     * @return The type of value that is achieved when this operator is executed.\n     */\n    public IValueType getConditionalOutputType(IVariable[] input);\n\n    /**\n     * Evaluate the given input values for this operator.\n     * @param input The ordered input values.\n     * @return The output value.\n     * @throws EvaluationException When something went wrong while evaluating.\n     */\n    public IValue evaluate(IVariable... input) throws EvaluationException;\n\n    /**\n     * @return The required input length.\n     */\n    public int getRequiredInputLength();\n\n    /**\n     * Check the given input value types for this operator.\n     * @param input The ordered input value types.\n     * @return An error or null if valid.\n     */\n    public MutableComponent validateTypes(IValueType[] input);\n\n    /**\n     * @return The render pattern for this operator inside the logic programmer.\n     */\n    public IConfigRenderPattern getRenderPattern();\n\n    /**\n     * Materialize this operator so that it can exist without any external references.\n     * @return The materialized operator.\n     * @throws EvaluationException if materialization fails because of a variable evaluation.\n     */\n    public IOperator materialize() throws EvaluationException;\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/operator/IOperatorRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.operator;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IOperatorVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * Registry for {@link IOperator}\n * @author rubensworks\n */\npublic interface IOperatorRegistry extends IRegistry, IVariableFacadeHandler<IOperatorVariableFacade> {\n\n    /**\n     * Register a new operator.\n     * @param operator The operator.\n     * @param <O> The operator type.\n     * @return The registered operator.\n     */\n    public <O extends IOperator> O register(O operator);\n\n    /**\n     * @return All registered operators.\n     */\n    public Collection<IOperator> getOperators();\n\n    /**\n     * Get the operator with the given name.\n     * @param operatorName The unique operator name.\n     * @return The corresponding operator or null.\n     */\n    public IOperator getOperator(Identifier operatorName);\n\n    /**\n     * Get the operators with the given input value types in that specific order.\n     * @param valueTypes The input value types.\n     * @return The corresponding operators.\n     */\n    public Collection<IOperator> getOperatorsWithInputTypes(IValueType... valueTypes);\n\n    /**\n     * Get the operators with the given output value type.\n     * @param valueType The output value type.\n     * @return The corresponding operators.\n     */\n    public Collection<IOperator> getOperatorsWithOutputType(IValueType valueType);\n\n    /**\n     * Get the operators in the given category.\n     * @param categoryName The name of the operator category.\n     * @return The corresponding operators.\n     */\n    public Collection<IOperator> getOperatorsInCategory(String categoryName);\n\n    /**\n     * Register an operator serializer.\n     * @param serializer The operator serializer.\n     */\n    public void registerSerializer(IOperatorSerializer serializer);\n\n    /**\n     * Serialize the given operator.\n     *\n     * @param valueOutput The value output.\n     * @param value       The operator to serialize.\n     */\n    public void serialize(ValueOutput valueOutput, IOperator value);\n\n    /**\n     * Deserialize the given operator value.\n     *\n     * @param valueInput The operator value to deserialize.\n     * @return The deserialized operator.\n     * @throws EvaluationException If an error occurs while deserializing.\n     */\n    public IOperator deserialize(ValueInput valueInput) throws EvaluationException;\n\n    /**\n     * @return Mapping of globally interaction names of operators to their instance.\n     */\n    public Map<String, IOperator> getGlobalInteractOperators();\n\n    /**\n     * @return Mapping of value types to locally interaction names of operators to their instance.\n     */\n    public Map<IValueType<?>, Map<String, IOperator>> getScopedInteractOperators();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/operator/IOperatorSerializer.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.operator;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\n\n/**\n * A serialization action for operators.\n * @param <O> The operator type\n * @author rubensworks\n */\npublic interface IOperatorSerializer<O extends IOperator> {\n\n    /**\n     * @param operator The operator to test.\n     * @return If this can serialize the given operator.\n     */\n    public boolean canHandle(IOperator operator);\n\n    /**\n     * @return The unique name of this serializer.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * Serialize the given operator.\n     *\n     * @param valueOutput\n     * @param operator    The operator to serialize.\n     */\n    public void serialize(ValueOutput valueOutput, O operator);\n\n    /**\n     * Deserialize the given operator value.\n     *\n     * @param valueInput\n     * @return The deserialized operator, null if deserialization failed.\n     * @throws EvaluationException If something goes wrong while deserializing\n     */\n    public O deserialize(ValueInput valueInput) throws EvaluationException;\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValue.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\n/**\n * An immutable value.\n * @author rubensworks\n */\npublic interface IValue {\n\n    /**\n     * @return The type of this variable.\n     */\n    public IValueType getType();\n\n    /**\n     * Safely downcast this value.\n     * @param valueType The type of value to cast to.\n     * @param <V> The actual value type.\n     * @return The downcasted value.\n     * @throws IValueCastRegistry.ValueCastException If this value could not be cast to the given type.\n     */\n    public <V extends IValue> V cast(IValueType<V> valueType) throws IValueCastRegistry.ValueCastException;\n\n    /**\n     * Safely downcast this value.\n     * @param valueType The type of value to cast to.\n     * @param <V> The actual value type.\n     * @return If this value can be casted to the target type.\n     */\n    public <V extends IValue> boolean canCast(IValueType<V> valueType);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueCastRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.InvalidValueTypeException;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * Registry for casting {@link IValue}.\n * @author rubensworks\n */\npublic interface IValueCastRegistry extends IRegistry {\n\n    public int size();\n\n    /**\n     * Register a mapping between to value types.\n     * @param from The source type.\n     * @param to The target type.\n     * @param mapping The mapping logic.\n     * @param <T1> The source type type.\n     * @param <T2> The target type type.\n     * @param <V1> The source type.\n     * @param <V2> The target type.\n     */\n    public <T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> void register(\n            T1 from, T2 to, IMapping<T1, T2, V1, V2> mapping);\n\n\n    /**\n     * Cast the given value to the given type.\n     * @param target The target type.\n     * @param value The value to cast.\n     * @param <T1> The source type type.\n     * @param <T2> The target type type.\n     * @param <V1> The source type.\n     * @param <V2> The target type.\n     * @return The cast value\n     * @throws ValueCastException If casting failed because the type mapping did not exist.\n     */\n    public <T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> V2 cast(T2 target, V1 value) throws ValueCastException;\n\n    /**\n     * Check if the the given value can be cast to the given type.\n     * @param target The target type.\n     * @param value The value to cast.\n     * @param <T1> The source type type.\n     * @param <T2> The target type type.\n     * @param <V1> The source type.\n     * @param <V2> The target type.\n     * @return If the value can be cast to the given type.\n     */\n    public <T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> boolean canCast(T2 target, V1 value);\n\n    public static interface IMapping<T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> {\n\n        public V2 cast(V1 value);\n\n    }\n\n    public static class ValueCastException extends InvalidValueTypeException {\n\n        public ValueCastException(IValueType from, IValueType to) {\n            super(Component.translatable(L10NValues.OPERATOR_ERROR_CAST_NOMAPPING, from, to));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueType.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Comparator;\nimport java.util.function.Consumer;\n\n/**\n * Type of value\n * @author rubensworks\n */\npublic interface IValueType<V extends IValue> {\n\n    public IValueTypeClient<V> getClient();\n\n    /**\n     * @return If this type is a category and should be handled accordingly.\n     */\n    public boolean isCategory();\n\n    /**\n     * @return If this type is an object type, otherwise it is a raw type.\n     */\n    public boolean isObject();\n\n    /**\n     * Create an immutable default value.\n     * @return The default value of this type.\n     */\n    public V getDefault();\n\n    /**\n     * @return The name of this type without any prefixes.\n     */\n    public String getTypeName();\n\n    /**\n     * @return The unique name for this value type, only used for internal storage.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * @return The unique name of this type that will also be used for display.\n     */\n    public String getTranslationKey();\n\n    /**\n     * Add tooltip lines for this aspect when hovered in a gui.\n     *\n     * @param tooltipAdder       The list to add lines to.\n     * @param appendOptionalInfo If shift-to-show info should be added.\n     * @param value              The value to show the tooltip for.\n     */\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo, @Nullable V value);\n\n    /**\n     * @param value The value\n     * @return A short string representation used in guis to show the value.\n     */\n    public MutableComponent toCompactString(V value);\n\n    /**\n     * @return The color that is used to identify this value type.\n     */\n    public int getDisplayColor();\n\n    /**\n     * @return The color that is used to identify this value type using MC formatting codes.\n     */\n    public ChatFormatting getDisplayColorFormat();\n\n    /**\n     * Check if the given type corresponds with this type.\n     * To check bidirectional, use {@link org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers#correspondsTo(IValueType, IValueType)}.\n     * @param valueType The value type to check correspondence with.\n     * @return If the given value type can be used with this value type.\n     */\n    public boolean correspondsTo(IValueType<?> valueType);\n\n    /**\n     * Serialize the given value.\n     *\n     * @param valueOutput Where to write the value to.\n     * @param value       The value to serialize.\n     */\n    public void serialize(ValueOutput valueOutput, V value);\n\n    /**\n     * Check if the given value can be deserialized.\n     *\n     * @param valueInput The value to deserialize.\n     * @return An error or null.\n     */\n    @Nullable\n    public Component canDeserialize(ValueInput valueInput);\n\n    /**\n     * Deserialize the given value.\n     *\n     * @param valueInput The value to deserialize.\n     * @return The deserialized value.\n     */\n    public V deserialize(ValueInput valueInput);\n\n    /**\n     * Materialize the given value so that it can exist without any external references.\n     * @param value The value to materialize.\n     * @return The materialized value.\n     * @throws EvaluationException if materialization fails because of a variable evaluation.\n     */\n    public V materialize(V value) throws EvaluationException;\n\n    /**\n     * Get the string representation of the given value.\n     * This is useful for cases when the value needs to be edited in a GUI.\n     *\n     * This corresponds to {@link #parseString(String)}.\n     *\n     * @param value A value.\n     * @return A string representation of the given value.\n     */\n    public String toString(V value);\n\n    /**\n     * Parse the given string representation of a value.\n     *\n     * This corresponds to {@link #toString(IValue)}.\n     *\n     * @param value A string representation of a value.\n     * @return A value.\n     * @throws EvaluationException If parsing failed.\n     */\n    public V parseString(String value) throws EvaluationException;\n\n    /**\n     * @return A new logic programmer element for this value type.\n     */\n    public IValueTypeLogicProgrammerElement createLogicProgrammerElement();\n\n    /**\n     * Attempt to cast the given value to a value of this value type.\n     * @param value A value of unknown type.\n     * @return The casted value.\n     * @throws EvaluationException If the incorrect value type was found.\n     */\n    public V cast(IValue value) throws EvaluationException;\n\n    /**\n     * Use this comparator for any comparisons with value types.\n     */\n    public static class ValueTypeComparator implements Comparator<IValueType<?>> {\n\n        private static ValueTypeComparator INSTANCE = null;\n\n        private ValueTypeComparator() {\n\n        }\n\n        public static ValueTypeComparator getInstance() {\n            if(INSTANCE == null) INSTANCE = new ValueTypeComparator();\n            return INSTANCE;\n        }\n\n        @Override\n        public int compare(IValueType<?> o1, IValueType<?> o2) {\n            return o1.getUniqueName().compareTo(o2.getUniqueName());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeCategory.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport java.util.Collection;\n\n/**\n * Abstraction layer above value type to categorize them so that a certain category can be used inside operators among other things.\n * @author rubensworks\n */\npublic interface IValueTypeCategory<V extends IValue> extends IValueType<V> {\n    /**\n     * @return The value types that are part of this category.\n     */\n    public Collection<IValueType<?>> getElements();\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeClient.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\n\n/**\n * @author rubensworks\n */\npublic interface IValueTypeClient<V extends IValue> {\n\n    /**\n     * Called during ISTER rendering of an variable item.\n     *\n     * @param value               The value to value to render.\n     * @param stack               The variable stack.\n     * @param transformType       Transform type.\n     * @param matrixStack         Matrix stack.\n     * @param submitNodeCollector Node collector.\n     * @param combinedLight       Lighting.\n     * @param combinedOverlay     Overlay.\n     */\n    public default void renderISTER(V value, ItemStack stack, ItemDisplayContext transformType, PoseStack matrixStack, SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeLightLevelRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.InvalidValueTypeException;\n\nimport javax.annotation.Nullable;\n\n/**\n * Registry for mapping value types to their light level calculator.\n * @author rubensworks\n */\npublic interface IValueTypeLightLevelRegistry extends IRegistry {\n\n    /**\n     * Register light level calculator for a value type.\n     * @param valueType The value type\n     * @param lightLevelCalculator The light level calculator.\n     * @param <L> The light level calculator type.\n     * @param <V> The value type.\n     * @return The registered light level calculator.\n     */\n    public <L extends ILightLevelCalculator<V>, V extends IValue> L register(IValueType<V> valueType, L lightLevelCalculator);\n\n    /**\n     * Get the light level calculator for the given value type.\n     * @param valueType The value type\n     * @param <V> The value type.\n     * @return The registered light level calculator.\n     */\n    @Nullable\n    public <V extends IValue> ILightLevelCalculator<V> getLightLevelCalculator(IValueType<V> valueType);\n\n    /**\n     * Get the light level calculator for the given value.\n     * @param value The value\n     * @param <V> The value type.\n     * @return The registered light level calculator.\n     * @throws InvalidValueTypeException If an error occured while autocasting.\n     */\n    public <V extends IValue> int getLightLevel(V value) throws InvalidValueTypeException;\n\n    public static interface ILightLevelCalculator<V extends IValue> {\n\n        public int getLightLevel(V value);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeListProxy.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\n\n/**\n * Proxy for a list.\n * @param <T> The list element type value type.\n * @param <V> The list element type.\n */\npublic interface IValueTypeListProxy<T extends IValueType<V>, V extends IValue> extends Iterable<V> {\n\n    /**\n     * @return The list length\n     * @throws EvaluationException If something went wrong wile getting an element.\n     */\n    public int getLength() throws EvaluationException;\n\n    /**\n     * Get the element at the given index.\n     * @param index The index.\n     * @return The element at the given index.\n     * @throws EvaluationException If something went wrong wile getting an element.\n     */\n    public V get(int index) throws EvaluationException;\n\n    /**\n     * @return The list element value type.\n     */\n    public T getValueType();\n\n    /**\n     * @return The proxy type name that must exist in the {@link IValueTypeListProxyFactoryTypeRegistry}\n     * so that this can be correctly (de)serialized.\n     */\n    public Identifier getName();\n\n    /**\n     * @return A short string representation used in guis to show the value.\n     */\n    public MutableComponent toCompactString();\n\n    /**\n     * @return If this is an infinite list.\n     */\n    public boolean isInfinite();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeListProxyFactoryTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.init.IRegistry;\n\n/**\n * Registry for list value type proxies.\n * @author rubensworks\n */\npublic interface IValueTypeListProxyFactoryTypeRegistry extends IRegistry {\n\n    /**\n     * Register a proxy factory by name.\n     * @param proxyFactory The proxy factory.\n     * @param <T> The list element type value type.\n     * @param <V> The list element type.\n     * @param <P> The proxy type.\n     * @param <F> The factory type.\n     * @return The registered instance.\n     */\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>, F extends IProxyFactory<T, V, P>> F register(F proxyFactory);\n\n    /**\n     * Get a proxy factory by name\n     * @param <T> The list element type value type.\n     * @param <V> The list element type.\n     * @param <P> The proxy type.\n     * @param name The name.\n     * @return The corresponding instance.\n     */\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> IProxyFactory<T, V, P> getFactory(Identifier name);\n\n    /**\n     * Serialize the given list proxy.\n     *\n     * @param <T>         The list element type value type.\n     * @param <V>         The list element type.\n     * @param <P>         The proxy type.\n     * @param valueOutput The value to output to.\n     * @param proxy       The proxy to serialize.\n     * @throws SerializationException If something goes wrong while serializing.\n     */\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> void serialize(ValueOutput valueOutput, P proxy) throws SerializationException;\n\n    /**\n     * Deserialize the given serialized list proxy to a list proxy instance.\n     *\n     * @param <T>        The list element type value type.\n     * @param <V>        The list element type.\n     * @param <P>        The proxy type.\n     * @param valueInput The serialized list proxy.\n     * @return The deserialized list proxy.\n     * @throws SerializationException If something goes wrong while serializing.\n     */\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> P deserialize(ValueInput valueInput) throws SerializationException;\n\n    /**\n     * Factory for a list proxy.\n     * @param <T> The list element type value type.\n     * @param <V> The list element type.\n     * @param <P> The proxy type.\n     */\n    public static interface IProxyFactory<T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> {\n\n        /**\n         * @return The unique indentifier of this proxy.\n         */\n        public Identifier getName();\n\n        /**\n         * Serialize the given value.\n         *\n         * @param valueOutput Where to serialize to.\n         * @param value       The value to serialize.\n         * @throws SerializationException If something goes wrong while serializing.\n         */\n        public void serialize(ValueOutput valueOutput, P value) throws SerializationException;\n\n        /**\n         * Deserialize the given value.\n         *\n         * @param valueInput The value to deserialize.\n         * @return The deserialized value.\n         * @throws SerializationException If something goes wrong while deserializing.\n         */\n        public P deserialize(ValueInput valueInput) throws SerializationException;\n\n    }\n\n    /**\n     * If something goes wrong while (de)serializing.\n     */\n    public static class SerializationException extends Exception {\n\n        public SerializationException(String message) {\n            super(message);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeNamed.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\n/**\n * A value type that has a localized name, mostly this type instance is an object.\n * @author rubensworks\n */\npublic interface IValueTypeNamed<V extends IValue> extends IValueType<V> {\n\n    public String getName(V a);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeNullable.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\n/**\n * A value type that can be null.\n * @author rubensworks\n */\npublic interface IValueTypeNullable<V extends IValue> extends IValueType<V> {\n\n    public boolean isNull(V a);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeNumber.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeString;\n\n/**\n * A numerical value type.\n * To allow for a good functioning, this requires all types to have mappings to each other in the {@link IValueCastRegistry}.\n * @author rubensworks\n */\npublic interface IValueTypeNumber<V extends IValue> extends IValueType<V>, IValueTypeNamed<V> {\n\n    /**\n     * Create an immutable default (zero) value.\n     * @return The zero value of this number type.\n     */\n    public V getDefault();\n\n    public boolean isZero(V a);\n    public boolean isOne(V a);\n    public V add(V a, V b);\n    public V subtract(V a, V b);\n    public V multiply(V a, V b);\n    public V divide(V a, V b);\n    public V max(V a, V b);\n    public V min(V a, V b);\n    public V increment(V a);\n    public V decrement(V a);\n    public V modulus(V a, V b);\n    public ValueTypeString.ValueString compact(V a);\n    public boolean greaterThan(V a, V b);\n    public boolean lessThan(V a, V b);\n    public ValueTypeInteger.ValueInteger round(V a);\n    public ValueTypeInteger.ValueInteger ceil(V a);\n    public ValueTypeInteger.ValueInteger floor(V a);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.item.IValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\n\nimport java.util.Collection;\n\n/**\n * Registry for {@link IValueType}\n * @author rubensworks\n */\npublic interface IValueTypeRegistry extends IRegistry, IVariableFacadeHandler<IValueTypeVariableFacade> {\n\n    public IValueTypeRegistryClient getClient();\n\n    /**\n     * Register a new value type.\n     * @param valueType The part type.\n     * @param <V> The value type.\n     * @param <T> The value type type.\n     * @return The registered value type.\n     */\n    public <V extends IValue, T extends IValueType<V>> T register(T valueType);\n\n    /**\n     * Register a new value category.\n     * This registration can be overwritten, so only the last registered category is remembered.\n     * @param category The category.\n     * @param <V> The value type.\n     * @param <T> The value type type.\n     * @return The registered category.\n     */\n    public <V extends IValue, T extends IValueTypeCategory<V>> T registerCategory(T category);\n\n    /**\n     * Get the value type by name.\n     * @param name The unique name.\n     * @return The value type or null if not found.\n     */\n    public IValueType getValueType(Identifier name);\n\n    /**\n     * @return All registered value types.\n     */\n    public Collection<IValueType> getValueTypes();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeRegistryClient.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.resources.Identifier;\n\nimport java.util.Collection;\n\n/**\n * @author rubensworks\n */\npublic interface IValueTypeRegistryClient {\n\n    /**\n     * Register a facadeModel resource location for the given value type.\n     * @param <V> The value type.\n     * @param <T> The value type type.\n     * @param valueType The value type.\n     * @param modelLocation The facadeModel resource location.\n     */\n    public <V extends IValue, T extends IValueType<V>> void registerValueTypeModel(T valueType, Identifier modelLocation);\n\n    /**\n     * Get the facadeModel resource location of the given value type.\n     *\n     * @param <V>       The value type.\n     * @param <T>       The value type type.\n     * @param valueType The value type.\n     * @return The unbaked facadeModel resource.\n     */\n    public <V extends IValue, T extends IValueType<V>> ItemModel.Unbaked getValueTypeModel(T valueType);\n\n    /**\n     * Get all registered facadeModel resource locations for the value types.\n     * @return All facadeModel resource locations.\n     */\n    public Collection<ItemModel.Unbaked> getValueTypeModels();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IValueTypeUniquelyNamed.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\n/**\n * A value type that has a unique name, mostly this type instance is an object.\n * @author rubensworks\n */\npublic interface IValueTypeUniquelyNamed<V extends IValue> extends IValueType<V> {\n\n    public String getUniqueName(V a);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IVariable.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\n\n/**\n * Facade through which a value can be retrieved.\n * @author rubensworks\n */\npublic interface IVariable<V extends IValue> extends IVariableInvalidateListener {\n\n    /**\n     * @return The type of value this variable provides.\n     */\n    public IValueType<V> getType();\n\n    /**\n     * @return The current value of this variable.\n     * @throws EvaluationException If something went wrong while evaluating\n     */\n    public V getValue() throws EvaluationException;\n\n    /**\n     * Add a dependency relation.\n     *\n     * This makes it so that when this variable gets invalidated,\n     * the given listener/variables also becomes invalidated.\n     *\n     * This invalidation should happen recursively for variables.\n     *\n     * This listener will be removed after the first invalidation.\n     * If needed, the listener can be re-attached after that.\n     *\n     * @param invalidateListener A listener for invalidations.\n     */\n    public void addInvalidationListener(IVariableInvalidateListener invalidateListener);\n\n    /**\n     * Remove a dependency relation.\n     *\n     * @param invalidateListener A listener for invalidations.\n     */\n    public void removeInvalidationListener(IVariableInvalidateListener invalidateListener);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/IVariableInvalidateListener.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\n/**\n * A listener for variable invalidations\n * @author rubensworks\n */\npublic interface IVariableInvalidateListener {\n\n    /**\n     * Called when a variable was invalidated.\n     */\n    public void invalidate();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/ValueDeseralizationContext.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.core.HolderLookup;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\n\n\n/**\n * @author rubensworks\n */\npublic record ValueDeseralizationContext(HolderLookup.Provider holderLookupProvider) {\n    public static ValueDeseralizationContext of(Level level) {\n        if (level == null) {\n            return new ValueDeseralizationContext(ServerLifecycleHooks.getCurrentServer().registryAccess());\n        }\n        return new ValueDeseralizationContext(level.registryAccess());\n    }\n\n    public static ValueDeseralizationContext of(HolderLookup.Provider holderLookupProvider) {\n        return new ValueDeseralizationContext(holderLookupProvider);\n    }\n\n    public static ValueDeseralizationContext ofClient() {\n        return ValueDeseralizationContextClient.ofClient();\n    }\n\n    public static ValueDeseralizationContext ofAllEnabled() {\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            return ofClient();\n        }\n        return new ValueDeseralizationContext(ServerLifecycleHooks.getCurrentServer().registryAccess());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/evaluate/variable/ValueDeseralizationContextClient.java",
    "content": "package org.cyclops.integrateddynamics.api.evaluate.variable;\n\nimport net.minecraft.client.Minecraft;\n\n/**\n * @author rubensworks\n */\npublic class ValueDeseralizationContextClient {\n    public static ValueDeseralizationContext ofClient() {\n        return ValueDeseralizationContext.of(Minecraft.getInstance().level);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/ingredient/IIngredientComponentHandler.java",
    "content": "package org.cyclops.integrateddynamics.api.ingredient;\n\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport javax.annotation.Nullable;\n\n/**\n * Handles the translation between {@link IngredientComponent} instances and {@link IValue}.\n * @param <VT> The value type.\n * @param <V> The value.\n * @param <T> The instance type.\n * @param <M> The matching condition parameter.\n * @author rubensworks\n */\npublic interface IIngredientComponentHandler<VT extends IValueType<V>, V extends IValue, T, M> {\n\n    /**\n     * @return The value type with which the component should be handled.\n     */\n    public VT getValueType();\n\n    /**\n     * @return The recipe component.\n     */\n    public IngredientComponent<T, M> getComponent();\n\n    /**\n     * Convert an instance to a value.\n     * @param instance A recipe component instance.\n     * @return A value.\n     */\n    public V toValue(@Nullable T instance);\n\n    /**\n     * Convert a value to an instance.\n     * @param value A value.\n     * @return A recipe component instance.\n     */\n    @Nullable\n    public T toInstance(V value);\n\n    /**\n     * Convert the given list of values to a compact string.\n     * To be used in things like tooltips.\n     * @param ingredientValue A list of ingredient values.\n     * @return A compact string representation.\n     */\n    default public Component toCompactString(V ingredientValue) {\n        return getValueType().toCompactString(ingredientValue);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/ingredient/IIngredientComponentHandlerRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.ingredient;\n\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport javax.annotation.Nullable;\nimport java.util.Set;\n\n/**\n * Registry for handling the translation between {@link IngredientComponent} instances and {@link IValue}.\n * @author rubensworks\n */\npublic interface IIngredientComponentHandlerRegistry extends IRegistry {\n\n    /**\n     * Register a new recipe component handler.\n     * @param handler The new handler.\n     * @param <VT> The value type.\n     * @param <V> The value.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter.\n     * @param <H> The handler type.\n     * @return The registered handler.\n     */\n    public <VT extends IValueType<V>, V extends IValue, T, M, H extends IIngredientComponentHandler<VT, V, T, M>> H register(H handler);\n\n    /**\n     * Get a handler by component type.\n     * @param component The component type.\n     * @param <VT> The value type.\n     * @param <V> The value.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter.\n     * @return The handler or null.\n     */\n    @Nullable\n    public <VT extends IValueType<V>, V extends IValue, T, M> IIngredientComponentHandler<VT, V, T, M> getComponentHandler(IngredientComponent<T, M> component);\n\n    /**\n     * @return The recipe components that have a handler.\n     */\n    public Set<IngredientComponent<?, ?>> getComponents();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/ingredient/IIngredientComponentStorageObservable.java",
    "content": "package org.cyclops.integrateddynamics.api.ingredient;\n\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.ingredient.collection.IIngredientCollection;\nimport org.cyclops.cyclopscore.ingredient.collection.diff.IngredientCollectionDiff;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\n\n/**\n * An observable ingredient component storage.\n * @param <T> The instance type.\n * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n * @author rubensworks\n */\npublic interface IIngredientComponentStorageObservable<T, M> {\n\n    /**\n     * @return The ingredient component type this storage applies to.\n     */\n    public IngredientComponent<T, M> getComponent();\n\n    /**\n     * Add an observer for listing to index change events.\n     * @param observer An index change observer.\n     */\n    public void addObserver(IIndexChangeObserver<T, M> observer);\n\n    /**\n     * Remove the given index change observer.\n     * This will silently fail if the given observer was not registered.\n     * @param observer An index change observer.\n     */\n    public void removeObserver(IIndexChangeObserver<T, M> observer);\n\n    /**\n     * Indicate that this network should observe starting somewhere in the future.\n     *\n     * This should be called when observer diffs are needed,\n     * but are not urgent.\n     *\n     * This is a more efficient variant of\n     * {@link IIngredientComponentStorageObservable#scheduleObservationForced(int, PartPos)}.\n     *\n     * This should be called each time that observations are needed,\n     * as this observable will reset the internal flag after each observation.\n     */\n    public void scheduleObservation();\n\n    /**\n     * Indicate that this network should observe starting in the next tick.\n     *\n     * This is a stricter variant of {@link IIngredientComponentStorageObservable#scheduleObservation()},\n     * and can only be done for a single position and channel.\n     *\n     * This should be called when observer diffs are needed in the next tick for the given position.\n     * @param channel The channel to force an observation in.\n     * @param pos The position to force an observation for.\n     */\n    public void scheduleObservationForced(int channel, PartPos pos);\n\n    /**\n     * @return If an observation should happen.\n     */\n    public boolean shouldObserve();\n\n    /**\n     * @param channel The channel to check for a forced observation in.\n     * @return If the given channel contains a forced observation scheduling that has not been processed yet.\n     */\n    public boolean isObservationForcedPending(int channel);\n\n    /**\n     * Manually run observer synchronously (even if async is allowed), if it should observe.\n     */\n    public void runObserverSync();\n\n    /**\n     * Get the last indexed storage at the given channel.\n     * @param channel A channel id.\n     * @return A channel index.\n     */\n    public IIngredientPositionsIndex<T, M> getChannelIndex(int channel);\n\n    /**\n     * An observer for listening to storage changes.\n     * @param <T> The instance type.\n     * @param <M> The match condition type.\n     */\n    public static interface IIndexChangeObserver<T, M> {\n        /**\n         * Called when a change event is emitted.\n         * @param event A storage change event.\n         */\n        public void onChange(StorageChangeEvent<T, M> event);\n    }\n\n    /**\n     * A storage change event.\n     * This is thrown for either additions or deletions,\n     * as identified by the change type.\n     * @param <T> The instance type.\n     * @param <M> The match condition type.\n     */\n    public static class StorageChangeEvent<T, M> {\n        private final int channel;\n        private final PrioritizedPartPos pos;\n        private final Change changeType;\n        private final boolean completeChange;\n        private final IIngredientCollection<T, M> instances;\n        private final boolean initialChange;\n\n        public StorageChangeEvent(int channel, PrioritizedPartPos pos,\n                                  Change changeType, boolean completeChange, IIngredientCollection<T, M> instances,\n                                  boolean initialChange) {\n            this.channel = channel;\n            this.pos = pos;\n            this.changeType = changeType;\n            this.completeChange = completeChange;\n            this.instances = instances;\n            this.initialChange = initialChange;\n        }\n\n        public int getChannel() {\n            return channel;\n        }\n\n        public PrioritizedPartPos getPos() {\n            return pos;\n        }\n\n        /**\n         * @return The type of change.\n         */\n        public Change getChangeType() {\n            return changeType;\n        }\n\n        /**\n         * @return If the change is complete.\n         *         In the case of additions, this means that the storage became completely full.\n         *         In the case of deletions, this means that the storage became completely empty.\n         */\n        public boolean isCompleteChange() {\n            return completeChange;\n        }\n\n        /**\n         * @return The instances that were added or removed.\n         */\n        public IIngredientCollection<T, M> getInstances() {\n            return instances;\n        }\n\n        /**\n         * @return If this change occurred during initialization of the observer.\n         */\n        public boolean isInitialChange() {\n            return initialChange;\n        }\n\n        @Override\n        public String toString() {\n            return String.format(\"[%s at %s(%s): %s]\", getChangeType().name(), getPos(), getChannel(), getInstances());\n        }\n\n        /**\n         * @return Get the collection diff from this event.\n         */\n        public IngredientCollectionDiff<T, M> getDiff() {\n            return new IngredientCollectionDiff<>(\n                    changeType == IIngredientComponentStorageObservable.Change.ADDITION ? getInstances() : null,\n                    changeType == IIngredientComponentStorageObservable.Change.DELETION ? getInstances() : null,\n                    false);\n        }\n    }\n\n    /**\n     * A type of change.\n     */\n    public static enum Change {\n        ADDITION,\n        DELETION\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/ingredient/IIngredientPositionsIndex.java",
    "content": "package org.cyclops.integrateddynamics.api.ingredient;\n\nimport org.cyclops.cyclopscore.ingredient.collection.IIngredientCollection;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\n\nimport java.util.Iterator;\n\n/**\n * An index that maps ingredients to positions that contain that instance.\n *\n * @param <T> An instance type.\n * @param <M> The matching condition parameter.\n */\npublic interface IIngredientPositionsIndex<T, M> extends IIngredientCollection<T, M> {\n\n    /**\n     * @return Get all positions that are not empty.\n     */\n    public Iterator<PartPos> getNonEmptyPositions();\n\n    /**\n     * Get all positions that have an instance that contain the given instance.\n     * @param instance An instance to match.\n     * @param matchFlags Instance match conditions.\n     * @return The positions.\n     */\n    public Iterator<PartPos> getPositions(T instance, M matchFlags);\n\n    /**\n     * Indicate that the given position contains the given position.\n     * @param instance An instance.\n     * @param pos A position.\n     */\n    public void addPosition(T instance, PrioritizedPartPos pos);\n\n    /**\n     * Indicate that the given instance is removed from the given position.\n     *\n     * This will not necessarily remove the given position,\n     * only if the total instance quantity becomes zero.\n     *\n     * @param instance An instance.\n     * @param pos A position.\n     */\n    public void removePosition(T instance, PrioritizedPartPos pos);\n\n    /**\n     * Get the available quantity for the given instance.\n     * @param instance An instance to match.\n     * @return The indexed quantity.\n     */\n    public long getQuantity(T instance);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/ingredient/capability/DefaultPositionedAddonsNetworkIngredientsHandler.java",
    "content": "package org.cyclops.integrateddynamics.api.ingredient.capability;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Function;\n\n/**\n * Default implementation of {@link IPositionedAddonsNetworkIngredientsHandler}.\n * @author rubensworks\n */\npublic class DefaultPositionedAddonsNetworkIngredientsHandler<T, M> implements IPositionedAddonsNetworkIngredientsHandler<T, M> {\n\n    private final Function<INetwork, IPositionedAddonsNetworkIngredients<T, M>> networkRetriever;\n\n    public DefaultPositionedAddonsNetworkIngredientsHandler(Function<INetwork, IPositionedAddonsNetworkIngredients<T, M>> networkRetriever) {\n        this.networkRetriever = networkRetriever;\n    }\n\n    @Nullable\n    @Override\n    public Optional<IPositionedAddonsNetworkIngredients<T, M>> getStorage(INetwork network) {\n        return Optional.of(networkRetriever.apply(network));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/ingredient/capability/IPositionedAddonsNetworkIngredientsHandler.java",
    "content": "package org.cyclops.integrateddynamics.api.ingredient.capability;\n\nimport java.util.Optional;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\n\n/**\n * A capability that retrieves the {@link IPositionedAddonsNetworkIngredients}\n * of an {@link org.cyclops.commoncapabilities.api.ingredient.IngredientComponent} in a network.\n *\n * @param <T> The instance type.\n * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n * @author rubensworks\n */\npublic interface IPositionedAddonsNetworkIngredientsHandler<T, M> {\n\n    /**\n     * Get the ingredient network in the given network,\n     * @param network The network.\n     * @return The optional ingredient component network.\n     */\n    public Optional<IPositionedAddonsNetworkIngredients<T, M>> getStorage(INetwork network);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IAspectVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\n\n/**\n * Variable facade for variables determined by part aspects.\n * @author rubensworks\n */\npublic interface IAspectVariableFacade extends IVariableFacade {\n\n    /**\n     * @return The part id this aspect is part of.\n     */\n    public int getPartId();\n\n    /**\n     * @return The used active in the selected part.\n     */\n    public IAspect getAspect();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IDelayVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\n/**\n * Variable facade for variables that are delayed.\n * @author rubensworks\n */\npublic interface IDelayVariableFacade extends IProxyVariableFacade {\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IOperatorVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport org.cyclops.integrateddynamics.api.evaluate.expression.IExpression;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\n\n/**\n * Variable facade for variables determined for operators based on other variables in the network determined by their id.\n * @author rubensworks\n */\npublic interface IOperatorVariableFacade extends IVariableFacade {\n\n    /**\n     * @return The operator this facade represents.\n     */\n    public IOperator getOperator();\n\n    /**\n     * @return The variable ids that define the inputs of this operator instance.\n     */\n    public int[] getVariableIds();\n\n    /**\n     * @return The expression that this operator applies.\n     */\n    public IExpression getExpression();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IProxyVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\n/**\n * Variable facade for variables that are proxied.\n * @author rubensworks\n */\npublic interface IProxyVariableFacade extends IVariableFacade {\n\n    /**\n     * @return The proxy id.\n     */\n    public int getProxyId();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IValueTypeVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\n\n/**\n * Variable facade for variables determined by part aspects.\n * @author rubensworks\n */\npublic interface IValueTypeVariableFacade<V extends IValue> extends IVariableFacade {\n\n    /**\n     * @return The value type of this variable.\n     */\n    public IValueType<V> getValueType();\n\n    /**\n     * @return The value contained in this variable.\n     */\n    public V getValue();\n\n    /**\n     * @return The variable referenced by this facade.\n     */\n    public IVariable<V> getVariable();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.Item;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Consumer;\n\n/**\n * A facade for retrieving a variable.\n * @author rubensworks\n */\npublic interface IVariableFacade {\n\n    public IVariableFacadeClient getClient();\n\n    /**\n     * @return The unique id for this facade.\n     */\n    public int getId();\n\n    /**\n     * @return The optional label for this facade.\n     */\n    @Nullable\n    public String getLabel();\n\n    /**\n     * Get the variable.\n     * @param <V> The value type.\n     * @param network The network used to look for the variable.\n     * @param partNetwork The part network used to look for the variable.\n     * @return The variable.\n     */\n    public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork);\n\n    /**\n     * @return If this is a valid reference.\n     */\n    public boolean isValid();\n\n    /**\n     * Check if this facade is valid, otherwise notify the validator of any errors.\n     * @param network The network used to look for the variable.\n     * @param partNetwork The part network used to look for the variable.\n     * @param validator The object to notify errors to.\n     * @param containingValueType The value type in which this variable facade is being used.\n     */\n    public void validate(INetwork network, IPartNetwork partNetwork, IValidator validator, IValueType containingValueType);\n\n    /**\n     * @return The output type of this variable facade.\n     */\n    public IValueType getOutputType();\n\n    /**\n     * Add information about this variable facade to the list.\n     *\n     * @param tooltipAdder The list to add lines to.\n     * @param context      The context.\n     */\n    public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context);\n\n    public static interface IValidator {\n\n        /**\n         * Set the current error for the given aspect.\n         *\n         * @param error The error to set, or null to clear.\n         */\n        public void addError(MutableComponent error);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\n\nimport javax.annotation.Nullable;\n\n/**\n * @author rubensworks\n */\npublic interface IVariableFacadeClient {\n\n    /**\n     * Handle the quads for the given baked facadeModel.\n     *\n     * @param variableModelBaked The baked facadeModel.\n     */\n    @Nullable\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked);\n\n    /**\n     * Called during ISTER rendering of an variable item.\n     *\n     * @param stack               The variable stack.\n     * @param transformType       Transform type.\n     * @param matrixStack         Matrix stack.\n     * @param submitNodeCollector Node collector.\n     * @param combinedLight       Lighting.\n     * @param combinedOverlay     Overlay.\n     */\n    public default void renderISTER(ItemStack stack, ItemDisplayContext transformType, PoseStack matrixStack, SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IVariableFacadeHandler.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\n\n/**\n * Handler for retrieving variable facades from items.\n * Must be registered in {@link IVariableFacadeHandlerRegistry}.\n * @param <F> The type of variable facade.\n * @author rubensworks\n */\npublic interface IVariableFacadeHandler<F extends IVariableFacade> {\n\n    /**\n     * @return The unique name of this type used to identity variables to this handler.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * Get the variable facade for the given tag.\n     *\n     * @param valueDeseralizationContext\n     * @param id The id that was read and needs to be inserted into the variable facade.\n     * @param tagCompound The tag containing information that can be read and used to form a variable facade.\n     * @return The variable facade\n     */\n    public F getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, int id, CompoundTag tagCompound);\n\n    /**\n     * Set the variable facade for the given tag.\n     *\n     * @param valueDeseralizationContext\n     * @param tagCompound                The tag that is used to write variable facade information to.\n     * @param variableFacade             The facade to write.\n     */\n    public void setVariableFacade(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tagCompound, F variableFacade);\n\n    /**\n     * If the given facade is created by this handler.\n     * @param variableFacade A facade.\n     * @return If it is an instance.\n     */\n    public boolean isInstance(IVariableFacade variableFacade);\n\n    /**\n     * If the given variable is created by this handler.\n     * @param variable A variable.\n     * @return If it is an instance.\n     */\n    public boolean isInstance(IVariable<?> variable);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IVariableFacadeHandlerRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\n\n/**\n * Registry for retrieving variable facade handlers.\n * @author rubensworks\n */\npublic interface IVariableFacadeHandlerRegistry extends IRegistry {\n\n    /**\n     * Register a new handler.\n     * @param variableFacadeHandler The handler.\n     */\n    public void registerHandler(IVariableFacadeHandler variableFacadeHandler);\n\n    /**\n     * Checks the type of the given tag and uses the corresponding handler to retrieve its variable facade.\n     *\n     * @param valueDeseralizationContext\n     * @param itemStack The item containing information that can be read and used to form a variable facade.\n     * @return The variable facade handled by the appropriate handler.\n     */\n    public IVariableFacade handle(ValueDeseralizationContext valueDeseralizationContext, ItemStack itemStack);\n\n    /**\n     * Checks the type of the given tag and uses the corresponding handler to retrieve its variable facade.\n     *\n     * @param valueDeseralizationContext\n     * @param tagCompound The tag containing information that can be read and used to form a variable facade.\n     * @return The variable facade handled by the appropriate handler.\n     */\n    public IVariableFacade handle(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tagCompound);\n\n    /**\n     * Find a handler by name.\n     * @param type The handler name.\n     * @return The handler.\n     */\n    @Nullable\n    public IVariableFacadeHandler getHandler(Identifier type);\n\n    /**\n     * @return All registered handler names.\n     */\n    public Collection<String> getHandlerNames();\n\n    /**\n     * Set the type of the given tag and uses the corresponding handler to write the variable facade.\n     *\n     * @param <F>                        The type of variable facade.\n     * @param valueDeseralizationContext\n     * @param tagCompound                The tag that is used to write variable facade information to.\n     * @param variableFacade             The facade to write.\n     * @param handler                    The handler for writing the facade.\n     */\n    public <F extends IVariableFacade> void write(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tagCompound, F variableFacade, IVariableFacadeHandler<F> handler);\n\n    /**\n     * Write the given variable facade to the given itemstack.\n     * @param itemStack The itemstack to write to.\n     * @param variableFacade The variable facade.\n     * @param variableFacadeHandler The variable facade handler.\n     * @return A copy of the given itemstack with the written variable facade.\n     * @param <F> The variable facade type.\n     */\n    public <F extends IVariableFacade> ItemStack writeVariableFacadeItem(ItemStack itemStack, F variableFacade, IVariableFacadeHandler<F> variableFacadeHandler);\n\n    /**\n     * Write a new variable facade to the given itemstack.\n     *\n     * @param <F> The variable facade type.\n     * @param generateId If a new id should be generated. Otherwise the previous facade id will be used or -1 if non existing.\n     * @param itemStack The itemstack to write to.\n     * @param variableFacadeHandler The variable facade handler.\n     * @param variableFacadeFactory A factory for creating a variable facade.\n     * @param level\n     * @param player The player crafting the item.\n     * @param blockState The block state in which the facade was created.\n     * @return A copy of the given itemstack with the written variable facade.\n     */\n    public <F extends IVariableFacade> ItemStack writeVariableFacadeItem(boolean generateId, ItemStack itemStack, IVariableFacadeHandler<F> variableFacadeHandler, IVariableFacadeFactory<F> variableFacadeFactory, Level level, @Nullable Player player, @Nullable BlockState blockState);\n\n    /**\n     * Create a new variable facade..\n     *\n     * @param <F> The variable facade type.\n     * @param generateId If a new id should be generated. Otherwise the previous facade id will be used or -1 if non existing.\n * @param itemStack The itemstack to write to.\n * @param variableFacadeHandler The variable facade handler.\n * @param variableFacadeFactory A factory for creating a variable facade.\n * @param valueDeseralizationContext\n     * @return The resulting variable facade.\n     */\n    public <F extends IVariableFacade> F writeVariableFacade(boolean generateId, ItemStack itemStack, IVariableFacadeHandler<F> variableFacadeHandler, IVariableFacadeFactory<F> variableFacadeFactory, ValueDeseralizationContext valueDeseralizationContext);\n\n    /**\n     * Copy the given itemstack and (possibly) assign a new id.\n     * @param generateId If a new id should be generated. Otherwise -1 will be used.\n     * @param itemStack The itemstack to copy, it is assumed to refer to a valid variable facade.\n     * @return A copy of the given itemstack with the written variable facade.\n     * @param <F> The variable facade type.\n     */\n    public <F extends IVariableFacade> ItemStack copy(boolean generateId, ItemStack itemStack);\n\n    /**\n     * Factory for creating variable facades.\n     * @param <F> The variable facade type.\n     */\n    public static interface IVariableFacadeFactory<F extends IVariableFacade> {\n\n        /**\n         * Create a new variable facade.\n         * @param generateId If a new id should be generated, otherwise id -1\n         * @return The new variable facade.\n         */\n        public F create(boolean generateId);\n\n        /**\n         * Create a new variable facade.\n         * @param id The id the facade should use.\n         * @return The new variable facade.\n         */\n        public F create(int id);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/IVariableFacadeHolder.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\n\n/**\n * Capability for items that can hold a {@link IVariableFacade}.\n * @author rubensworks\n */\npublic interface IVariableFacadeHolder {\n\n    /**\n     * @return The held variable facade.\n     * @param valueDeseralizationContext\n     */\n    public IVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/item/TagPathElement.java",
    "content": "package org.cyclops.integrateddynamics.api.item;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.util.ProblemReporter;\n\n/**\n * @author rubensworks\n */\npublic record TagPathElement(CompoundTag compoundTag) implements ProblemReporter.PathElement {\n    @Override\n    public String get() {\n        return \"tag@\" + compoundTag.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/logicprogrammer/IConfigRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.api.logicprogrammer;\n\nimport org.apache.commons.lang3.tuple.Pair;\n\n/**\n * Render pattern for elements inside the logic programmer.\n * @author rubensworks\n */\npublic interface IConfigRenderPattern {\n\n    /**\n     * @return Total width.\n     */\n    public int getWidth();\n\n    /**\n     * @return Total height.\n     */\n    public int getHeight();\n\n    /**\n     * @return Top-left positions of the slots to render.\n     */\n    public Pair<Integer, Integer>[] getSlotPositions();\n\n    /**\n     * @return Top-left position of the symbol to render.\n     */\n    public Pair<Integer, Integer> getSymbolPosition();\n\n    public static class Base implements IConfigRenderPattern {\n\n        private final int width, height;\n        private final Pair<Integer, Integer>[] slotPositions;\n        private final Pair<Integer, Integer> symbolPosition;\n\n        public Base(int width, int height, Pair<Integer, Integer>[] slotPositions, Pair<Integer, Integer> symbolPosition) {\n            this.width = width;\n            this.height = height;\n            this.slotPositions = slotPositions;\n            this.symbolPosition = symbolPosition;\n        }\n\n        public int getWidth() {\n            return this.width;\n        }\n\n        public int getHeight() {\n            return this.height;\n        }\n\n        public Pair<Integer, Integer>[] getSlotPositions() {\n            return this.slotPositions;\n        }\n\n        public Pair<Integer, Integer> getSymbolPosition() {\n            return this.symbolPosition;\n        }\n\n    }\n\n    public static final IConfigRenderPattern NONE = new IConfigRenderPattern.Base(100, 22, new Pair[0], null);\n    public static final IConfigRenderPattern NONE_CANVAS_WIDE = new IConfigRenderPattern.Base(158, 89, new Pair[0], null);\n    public static final IConfigRenderPattern NONE_CANVAS = new IConfigRenderPattern.Base(136, 89, new Pair[0], null);\n    public static final IConfigRenderPattern SINGLE_SLOT = new IConfigRenderPattern.Base(22, 22, new Pair[]{Pair.of(2, 2)}, null);\n    public static final IConfigRenderPattern RECIPE = new IConfigRenderPattern.Base(136, 89, new Pair[]{\n            // Items in\n            Pair.of(2, 2), Pair.of(20, 2), Pair.of(38, 2),\n            Pair.of(2, 20), Pair.of(20, 20), Pair.of(38, 20),\n            Pair.of(2, 38), Pair.of(20, 38), Pair.of(38, 38),\n            // Fluid in\n            Pair.of(2, 58),\n            // Items out\n            Pair.of(100, 2),\n            Pair.of(100, 20),\n            Pair.of(100, 38),\n            // Fluid out\n            Pair.of(82, 58),\n    }, null);\n    public static final IConfigRenderPattern INFIX = new IConfigRenderPattern.Base(100, 22, new Pair[]{Pair.of(2, 2), Pair.of(80, 2)}, Pair.of(50, 2));\n    public static final IConfigRenderPattern INFIX_LONG = new IConfigRenderPattern.Base(120, 22, new Pair[]{Pair.of(2, 2), Pair.of(100, 2)}, Pair.of(59, 2));\n    public static final IConfigRenderPattern INFIX_VERYLONG = new IConfigRenderPattern.Base(140, 22, new Pair[]{Pair.of(2, 2), Pair.of(120, 2)}, Pair.of(69, 2));\n    public static final IConfigRenderPattern PREFIX_1 = new IConfigRenderPattern.Base(40, 22, new Pair[]{Pair.of(20, 2)}, Pair.of(10, 2));\n    public static final IConfigRenderPattern PREFIX_1_LONG = new IConfigRenderPattern.Base(100, 22, new Pair[]{Pair.of(80, 2)}, Pair.of(40, 2));\n    public static final IConfigRenderPattern INFIX_2 = new IConfigRenderPattern.Base(120, 22, new Pair[]{Pair.of(2, 2), Pair.of(80, 2), Pair.of(100, 2)}, Pair.of(50, 2));\n    public static final IConfigRenderPattern INFIX_2_LONG = new IConfigRenderPattern.Base(140, 22, new Pair[]{Pair.of(2, 2), Pair.of(100, 2), Pair.of(120, 2)}, Pair.of(60, 2));\n    public static final IConfigRenderPattern INFIX_2_VERYLONG = new IConfigRenderPattern.Base(160, 22, new Pair[]{Pair.of(2, 2), Pair.of(130, 2), Pair.of(140, 2)}, Pair.of(70, 2));\n    public static final IConfigRenderPattern INFIX_2_LATE = new IConfigRenderPattern.Base(120, 22, new Pair[]{Pair.of(2, 2), Pair.of(22, 2), Pair.of(100, 2)}, Pair.of(70, 2));\n    public static final IConfigRenderPattern PREFIX_2 = new IConfigRenderPattern.Base(80, 22, new Pair[]{Pair.of(40, 2), Pair.of(60, 2)}, Pair.of(20, 2));\n    public static final IConfigRenderPattern PREFIX_2_LONG = new IConfigRenderPattern.Base(120, 22, new Pair[]{Pair.of(80, 2), Pair.of(100, 2)}, Pair.of(40, 2));\n    public static final IConfigRenderPattern INFIX_3 = new IConfigRenderPattern.Base(140, 22, new Pair[]{Pair.of(2, 2), Pair.of(80, 2), Pair.of(100, 2), Pair.of(120, 2)}, Pair.of(45, 2));\n    public static final IConfigRenderPattern PREFIX_3 = new IConfigRenderPattern.Base(120, 22, new Pair[]{Pair.of(60, 2), Pair.of(80, 2), Pair.of(100, 2)}, Pair.of(30, 2));\n    public static final IConfigRenderPattern PREFIX_3_LONG = new IConfigRenderPattern.Base(140, 22, new Pair[]{Pair.of(80, 2), Pair.of(100, 2), Pair.of(120, 2)}, Pair.of(40, 2));\n    public static final IConfigRenderPattern SUFFIX_1 = new IConfigRenderPattern.Base(40, 22, new Pair[]{Pair.of(6, 2)}, Pair.of(31, 2));\n    public static final IConfigRenderPattern SUFFIX_1_LONG = new IConfigRenderPattern.Base(120, 22, new Pair[]{Pair.of(2, 2)}, Pair.of(70, 2));\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/logicprogrammer/ILogicProgrammerElement.java",
    "content": "package org.cyclops.integrateddynamics.api.logicprogrammer;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.inventory.ContainerInput;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.inventory.slot.SlotExtended;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElement;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\n\n/**\n * An element instantiation inside the logic programmer.\n * @param <G> The type of gui.\n * @param <C> The type of container.\n * @param <S> The sub gui box type.\n * @author rubensworks\n */\npublic interface ILogicProgrammerElement<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu, GIC extends ILogicProgrammerElementClient<S, G, C>>\n        extends IGuiInputElement<S, G, C, GIC> {\n\n    /**\n     * @return The element type.\n     */\n    public ILogicProgrammerElementType getType();\n\n    /**\n     * @return The string used to match regex searching.\n     */\n    public String getMatchString();\n\n    /**\n     * If the given value type matches with this element's input.\n     * @param valueType The value type to match.\n     * @return If it matches\n     */\n    public boolean matchesInput(IValueType<?> valueType);\n\n    /**\n     * If the given value type matches with this element's output.\n     * @param valueType The value type to match.\n     * @return If it matches\n     */\n    public boolean matchesOutput(IValueType<?> valueType);\n\n    /**\n     * Create a temporary input slot for this element.\n     * The number of slots depends on the provided render pattern\n     * return by {@link #getRenderPattern()}.\n     * @param temporaryInputSlots The inventory behind this slot.\n     * @param slotId The slot id.\n     * @param x The X position for this slot.\n     * @param y The Y position for this slot.\n     * @return The created slot.\n     */\n    default Slot createSlot(Container temporaryInputSlots, int slotId, int x, int y) {\n        return createSlotDefault(this, temporaryInputSlots, slotId, x, y);\n    }\n\n    public static Slot createSlotDefault(ILogicProgrammerElement logicProgrammerElement, Container temporaryInputSlots, int slotId, int x, int y) {\n        SlotExtended slot = new SlotExtended(temporaryInputSlots, slotId, x, y) {\n            @Override\n            public boolean mayPlace(ItemStack itemStack) {\n                return logicProgrammerElement.isItemValidForSlot(slotId, itemStack);\n            }\n        };\n        slot.setPhantom(true);\n        return slot;\n    }\n\n    /**\n     * Called when an input item slot has been updated.\n     * @param player\n     * @param slotId The slot id.\n     * @param itemStack The itemstack currently in the slot, can be null.\n     */\n    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack);\n\n    /**\n     * @return If this element can be written to an item in its current state.\n     */\n    public boolean canWriteElementPre();\n\n    /**\n     * The stack to write the current state of this element to.\n     * @param player The player that is writing the element.\n     * @param itemStack The stack to write to.\n     * @return The resulting itemstack.\n     */\n    public ItemStack writeElement(Player player, ItemStack itemStack);\n\n    /**\n     * The variable facade to load onto this element.\n     *\n     * @param variableFacade        The variable facade to load.\n     */\n    public void loadElement(IVariableFacade variableFacade);\n\n    /**\n     * If this element in its current state can be deactivated because of another item being inserted into the\n     * write slot.\n     * @return If this element can be deactivated.\n     */\n    public boolean canCurrentlyReadFromOtherItem();\n\n    /**\n     * @param variableFacade A variable facade\n     * @return If this element corresponds to the given variable facade.\n     */\n    public boolean isFor(IVariableFacade variableFacade);\n\n    /**\n     * Check if the given item can be inserted into the given slot.\n     * @param slotId The slot id.\n     * @param itemStack The item that will be inserted.\n     * @return If it can be inserted.\n     */\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack);\n\n    /**\n     * Called when a player clicks on a slot.\n     * @param slotId The slot id.\n     * @param slot The slot.\n     * @param mouseButton The mouse buttong id.\n     * @param clickType The click type.\n     * @param player The clicking player.\n     * @return If further processing of the clicking should stop.\n     */\n    boolean slotClick(int slotId, Slot slot, int mouseButton, ContainerInput clickType, Player player);\n\n    /**\n     * @return The max stacksize.\n     */\n    public int getItemStackSizeLimit();\n\n    /**\n     * Set the currently stored value in the given container.\n     * This is only relevant to values that use things like slots, which must be updated server-side as well.\n     * @param container The container to put the currently stored value in.\n     */\n    public void setValueInContainer(C container);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/logicprogrammer/ILogicProgrammerElementClient.java",
    "content": "package org.cyclops.integrateddynamics.api.logicprogrammer;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementClient;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\n\n/**\n * @author rubensworks\n */\npublic interface ILogicProgrammerElementClient<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu> extends IGuiInputElementClient<S, G, C> {\n\n    /**\n     * @param subGui The corresponding sub gui of this element.\n     * @return If this element has the active focus. For typing and things like that.\n     */\n    public boolean isFocused(S subGui);\n\n    /**\n     * Set the focus of this element.\n     * @param subGui The corresponding sub gui of this element.\n     * @param focused If it must be focused.\n     */\n    public void setFocused(S subGui, boolean focused);\n\n    /**\n     * Set the currently stored value in the given sub gui.\n     * This is useful when the gui is reused for multiple elements where the actual value is stored in this element.\n     * @param subGui The sub gui to put the currently stored value in.\n     */\n    public void setValueInGui(S subGui);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/logicprogrammer/ILogicProgrammerElementType.java",
    "content": "package org.cyclops.integrateddynamics.api.logicprogrammer;\n\nimport net.minecraft.resources.Identifier;\n\nimport java.util.List;\n\n/**\n * Indicates a type of logic programmer element.\n * This creates one or more {@link ILogicProgrammerElement}.\n * Must be registered in {@link ILogicProgrammerElementTypeRegistry}.\n * @author rubensworks\n */\npublic interface ILogicProgrammerElementType<E extends ILogicProgrammerElement> {\n\n    /**\n     * Get the element from the given name.\n     * @param name The name, already namespaced for this type.\n     * @return The element.\n     */\n    public E getByName(Identifier name);\n\n    /**\n     * Get the name from given element, no need to namespace.\n     * @param element The element.\n     * @return The unique name.\n     */\n    public Identifier getName(E element);\n\n    /**\n     * @return Unique name.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * @return All the elements this type can have.\n     */\n    public List<E> createElements();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/logicprogrammer/ILogicProgrammerElementTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.logicprogrammer;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.init.IRegistry;\n\nimport java.util.List;\n\n/**\n * Registry for {@link ILogicProgrammerElementType}.\n * @author rubensworks\n */\npublic interface ILogicProgrammerElementTypeRegistry extends IRegistry {\n\n    /**\n     * Register a new type.\n     * @param type The type to register.\n     * @param <E> The type of type.\n     * @return The registered type\n     */\n    public <E extends ILogicProgrammerElementType> E addType(E type);\n\n    /**\n     * @return All registered types.\n     */\n    public List<ILogicProgrammerElementType> getTypes();\n\n    /**\n     * Get the type by name.\n     * @param name The name.\n     * @return The type.\n     */\n    public ILogicProgrammerElementType getType(Identifier name);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/logicprogrammer/IValueTypeLogicProgrammerElement.java",
    "content": "package org.cyclops.integrateddynamics.api.logicprogrammer;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport javax.annotation.Nullable;\n\n/**\n * An element instantiation of a value type inside the logic programmer.\n * @param <G> The type of gui.\n * @param <C> The type of container.\n * @param <S> The sub gui box type.\n * @author rubensworks\n */\npublic interface IValueTypeLogicProgrammerElement<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu, GIC extends ILogicProgrammerElementClient<S, G, C>> extends ILogicProgrammerElement<S, G, C, GIC> {\n\n    /**\n     * @return The value type of this element.\n     */\n    public IValueType<?> getValueType();\n\n    /**\n     * @return The current value.\n     */\n    public IValue getValue();\n\n    /**\n     * @param value                 The new value.\n     */\n    public void setValue(IValue value);\n\n    /**\n     * @return Create an inner gui element for modifying the value, may be null if it doesn't apply.\n     * @param <G2> The type of gui.\n     * @param <C2> The type of container.\n     */\n    @Nullable\n    public <G2 extends Screen, C2 extends AbstractContainerMenu> IGuiInputElementValueType<?, G2, C2, ?> createInnerGuiElement();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/AttachCapabilitiesEventNetwork.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.neoforged.bus.api.Event;\nimport net.neoforged.fml.event.IModBusEvent;\nimport net.neoforged.neoforge.capabilities.ICapabilityProvider;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * Event for when an {@link INetwork} is being constructed.\n * Next to capabilities, also {@link IFullNetworkListener}'s can be added to the network.\n * @author rubensworks\n */\npublic class AttachCapabilitiesEventNetwork extends Event implements IModBusEvent {\n\n    private final INetwork network;\n    private final List<IFullNetworkListener> fullNetworkListeners;\n    private final Map<NetworkCapability<?>, List<ICapabilityProvider<INetwork, Void, ?>>> providers;\n\n    public AttachCapabilitiesEventNetwork(INetwork network) {\n        this.network = network;\n        this.fullNetworkListeners = Lists.newArrayList();\n    this.providers = Maps.newIdentityHashMap();\n    }\n\n    public INetwork getNetwork() {\n        return this.network;\n    }\n\n    public void addFullNetworkListener(IFullNetworkListener fullNetworkListener) {\n        this.fullNetworkListeners.add(fullNetworkListener);\n    }\n\n    public List<IFullNetworkListener> getFullNetworkListeners() {\n        return fullNetworkListeners;\n    }\n\n    public <T> void register(\n            NetworkCapability<T> capability,\n            ICapabilityProvider<INetwork, Void, T> provider\n    ) {\n        Objects.requireNonNull(provider);\n        List<ICapabilityProvider<INetwork, Void, ?>> list = providers.computeIfAbsent(capability, k -> Lists.newArrayList());\n        list.add(provider);\n    }\n\n    public Map<NetworkCapability<?>, List<ICapabilityProvider<INetwork, Void, ?>>> getProviders() {\n        return providers;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/FullNetworkListenerAdapter.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\n\n/**\n * Default implementation of {@link IFullNetworkListener}.\n * @author rubensworks\n */\npublic class FullNetworkListenerAdapter implements IFullNetworkListener {\n    @Override\n    public boolean addNetworkElement(INetworkElement element, boolean networkPreinit) {\n        return true;\n    }\n\n    @Override\n    public boolean removeNetworkElementPre(INetworkElement element) {\n        return true;\n    }\n\n    @Override\n    public void removeNetworkElementPost(INetworkElement element, BlockState blockState, BlockEntity blockEntity) {\n\n    }\n\n    @Override\n    public void kill() {\n\n    }\n\n    @Override\n    public void updateGuaranteed() {\n\n    }\n\n    @Override\n    public void update() {\n\n    }\n\n    @Override\n    public boolean removePathElement(IPathElement pathElement, Direction side, BlockState blockState, BlockEntity blockEntity) {\n        return true;\n    }\n\n    @Override\n    public void afterServerLoad() {\n\n    }\n\n    @Override\n    public void beforeServerStop() {\n\n    }\n\n    @Override\n    public boolean canUpdate(INetworkElement element) {\n        return true;\n    }\n\n    @Override\n    public void postUpdate(INetworkElement element) {\n\n    }\n\n    @Override\n    public void onSkipUpdate(INetworkElement element) {\n\n    }\n\n    @Override\n    public void invalidateElement(INetworkElement element) {\n\n    }\n\n    @Override\n    public void revalidateElement(INetworkElement element) {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IEnergyConsumingNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\n/**\n * An energy consuming network element.\n * @author rubensworks\n */\npublic interface IEnergyConsumingNetworkElement extends INetworkElement {\n\n    /**\n     * @return The energy consumption rate of this part for the given state.\n     */\n    public int getConsumptionRate();\n\n    /**\n     * Called after the element was updated or not.\n     * If the update was not called, this can be because the network did not contain\n     * enough energy to let this element work.\n     * @param network The network.\n     * @param updated If the {@link INetworkElement#update(INetwork)} was called.\n     */\n    public void postUpdate(INetwork network, boolean updated);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IEnergyNetwork.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\n/**\n * A network capability that holds energy.\n * @author rubensworks\n */\npublic interface IEnergyNetwork extends IPositionedAddonsNetworkIngredients<Long, Boolean> {\n\n    /**\n     * @return The current network consumption rate.\n     */\n    public int getConsumptionRate();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IEventListenableNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport java.util.Optional;\n\n/**\n * Network elements that can delegate to elements that are automatically subscribed to the\n * {@link org.cyclops.integrateddynamics.api.network.event.INetworkEventBus}.\n * @param <D> The type of delegate that will be used as event listener for this network. Could be self or null.\n * @author rubensworks\n */\npublic interface IEventListenableNetworkElement<D extends INetworkEventListener<?>> extends INetworkElement {\n\n    /**\n     * This listener will never be saved as an instance, this network element is always used as delegator to this listener.\n     * @return The optional event listener.\n     */\n    public Optional<D> getNetworkEventListener();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IFullNetworkListener.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\n\n/**\n * This should be implemented on network capabilities that wish to listen to all network events.\n * @author rubensworks\n */\npublic interface IFullNetworkListener {\n\n    /**\n     * Add a given network element to the network\n     * Also checks if it can tick and will handle it accordingly.\n     * @param element The network element.\n     * @param networkPreinit If the network is still in the process of being initialized.\n     * @return If the addition succeeded.\n     */\n    public boolean addNetworkElement(INetworkElement element, boolean networkPreinit);\n\n    /**\n     * Checks if the given network element can be removed from the network\n     * @param element The network element.\n     * @return If the element was can be removed from the network.\n     */\n    public boolean removeNetworkElementPre(INetworkElement element);\n\n    /**\n     * Remove a given network element from the network.\n     * Also removed its tickable instance.\n     *\n     * @param element     The network element.\n     * @param blockState\n     * @param blockEntity\n     */\n    public void removeNetworkElementPost(INetworkElement element, BlockState blockState, BlockEntity blockEntity);\n\n    /**\n     * Terminate the network elements for this network.\n     */\n    public void kill();\n\n    /**\n     * This network updating should be called each tick.\n     */\n    public void update();\n\n    /**\n     * This guaranteed network updating should be called each tick, even in safe-mode.\n     */\n    public void updateGuaranteed();\n\n    /**\n     * Remove the given path element from the network.\n     * If the path element had any network elements registered in the network, these will be killed and removed as well.\n     *\n     * @param pathElement The path element.\n     * @param side        The side.\n     * @param state       The block state container.\n     * @param blockEntity\n     * @return If the path element was removed.\n     */\n    public boolean removePathElement(IPathElement pathElement, Direction side, BlockState state, BlockEntity blockEntity);\n\n    /**\n     * Called when the server loaded this network.\n     * This is the time to notify all network elements of this network.\n     */\n    public void afterServerLoad();\n\n    /**\n     * Called when the server will save this network before stopping.\n     * This is the time to notify all network elements of this network.\n     */\n    public void beforeServerStop();\n\n    /**\n     * If the given element can update.\n     * @param element The network element.\n     * @return If it can update.\n     */\n    public boolean canUpdate(INetworkElement element);\n\n    /**\n     * Called after a network element's update was called.\n     * @param element The network element.\n     */\n    public void postUpdate(INetworkElement element);\n\n    /**\n     * When the given element is not being updated because {@link INetwork#canUpdate(INetworkElement)}\n     * returned false.\n     * @param element The element that is not being updated.\n     */\n    public void onSkipUpdate(INetworkElement element);\n\n    /**\n     * Invalidate the given element.\n     * Called when the element's chunk is being unloaded.\n     * @param element The network element to invalidate.\n     */\n    public void invalidateElement(INetworkElement element);\n\n    /**\n     * Revalidate the given element.\n     * Called when the element's chunk is being reloaded.\n     * @param element The network element to invalidate.\n     */\n    public void revalidateElement(INetworkElement element);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IIdentifiableNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.resources.Identifier;\n\n/**\n * A network element that is identifiable within a certain group.\n * Element id's must be unique within the given group.\n * @author rubensworks\n */\npublic interface IIdentifiableNetworkElement {\n\n    /**\n     * @return The unique id of this element.\n     */\n    public int getId();\n\n    /**\n     * @return A group within which the element is unique.\n     */\n    public Identifier getGroup();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetwork.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEventBus;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\n\nimport java.util.Optional;\nimport java.util.Set;\n\n/**\n * A network can hold a set of {@link INetworkElement}s.\n * Note that this network only contains references to the relevant data, it does not contain the actual information.\n * @author rubensworks\n */\npublic interface INetwork extends IFullNetworkListener {\n\n    /**\n     * @return If this network and its elements have been fully initialized.\n     */\n    public boolean isInitialized();\n\n    /**\n     * @return The event bus for this network.\n     */\n    public INetworkEventBus getEventBus();\n\n    /**\n     * Add a given network element to the tickable elements set.\n     * @param element The network element.\n     */\n    public void addNetworkElementUpdateable(INetworkElement element);\n\n    /**\n     * Remove given network element from the tickable elements set.\n     * @param element The network element.\n     */\n    public void removeNetworkElementUpdateable(INetworkElement element);\n\n    /**\n     * Set the priority and channel of the given network element.\n     * @param element The network element.\n     * @param priority The new priority\n     * @param channel The new channel\n     */\n    public void setPriorityAndChannel(INetworkElement element, int priority, int channel);\n\n    /**\n     * Kills the network is it had no more network elements.\n     * @return If the network was killed.\n     */\n    public boolean killIfEmpty();\n\n    /**\n     * @return The network elements.\n     */\n    public Set<INetworkElement> getElements();\n\n    /**\n     * @return If this network has been killed.\n     */\n    public boolean isKilled();\n\n    /**\n     * @return If the network has changed structure or elements in the last tick.\n     */\n    public boolean hasChanged();\n\n    /**\n     * @return The number of cables in the network.\n     */\n    public int getCablesCount();\n\n    /**\n     * Get the last tick duration of the given network element.\n     * @param networkElement The networkelement\n     * @return Duration in nanoseconds\n     */\n    public long getLastSecondDuration(INetworkElement networkElement);\n\n    /**\n     * Reset the last second duration counts.\n     */\n    public void resetLastSecondDurations();\n\n    /**\n     * @return If this network has crashed.\n     */\n    public boolean isCrashed();\n\n    /**\n     * @param crashed The new crashed field.\n     */\n    public void setCrashed(boolean crashed);\n\n    /**\n     * Get the given capability.\n     * @param <T> The capability type.\n     * @param capability The capability to get.\n     * @return The optional capability instance.\n     */\n    public <T> Optional<T> getCapability(NetworkCapability<T> capability);\n\n    /**\n     * Invalidate the given element.\n     * This should be called when the element's chunk is being unloaded.\n     * @param element The network element to invalidate.\n     */\n    public void invalidateElement(INetworkElement element);\n\n    /**\n     * Revalidate the given element.\n     * This should be called when the element's chunk is being reloaded.\n     * @param element The network element to invalidate.\n     */\n    public void revalidateElement(INetworkElement element);\n\n    /**\n     * If the network element has not been invalidated.\n     * @param element A network element.\n     * @return If it is valid.\n     */\n    public boolean isValid(INetworkElement element);\n\n    /**\n     * @param sidedPathElement A sided path element.\n     * @return If this network contains the given sided path element.\n     */\n    public boolean containsSidedPathElement(ISidedPathElement sidedPathElement);\n\n    /**\n     * @return All registered network listeners.\n     */\n    public IFullNetworkListener[] getFullNetworkListeners();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkCarrier.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport javax.annotation.Nullable;\n\n/**\n * Capability for holding a network.\n * @author rubensworks\n */\npublic interface INetworkCarrier {\n\n    /**\n     * Tell the container it is part of the given network.\n     * @param network The network.\n     */\n    public void setNetwork(@Nullable INetwork network);\n\n    /**\n     * Get the current container network. Can be null.\n     * @return The network.\n     */\n    public INetwork getNetwork();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkCraftingHandler.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\n\n/**\n * A handler that adds crafting capabilities to networks.\n * @author rubensworks\n */\npublic interface INetworkCraftingHandler {\n\n    /**\n     * Check if the given instance is being crafted.\n     * @param network The network to craft in.\n     * @param ingredientsNetwork The ingredients network to craft in.\n     * @param channel A channel.\n     * @param ingredientComponent The ingredient component.\n     * @param instance The instance to craft.\n     * @param matchCondition The condition under which the instance should be crafted.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n     * @return If a crafting job exists for the given instance.\n     */\n    public <T, M> boolean isCrafting(INetwork network, IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork, int channel,\n                                     IngredientComponent<T, M> ingredientComponent, T instance, M matchCondition);\n\n    /**\n     * Check if this handler is applicable for the given channel.\n     * @param network The network to craft in.\n     * @param ingredientsNetwork The ingredients network to craft in.\n     * @param channel A channel.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n     * @return If we can craft in the given channel.\n     */\n    public <T, M> boolean canCraft(INetwork network, IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork, int channel);\n\n    /**\n     * Start a crafting job for the given instance in the given channel.\n     * @param network The network to craft in.\n     * @param ingredientsNetwork The ingredients network to craft in.\n     * @param channel A channel.\n     * @param ingredientComponent The ingredient component.\n     * @param instance The instance to craft.\n     * @param matchCondition The condition under which the instance should be crafted.\n     * @param ignoreExistingJobs If running jobs for the given instance should be ignored,\n     *                           and a new job should be started anyways.\n     *                           If true, and a job already existed, then this method MUST return true.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n     * @return If a crafting job could be started.\n     */\n    public <T, M> boolean craft(INetwork network, IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork, int channel,\n                                IngredientComponent<T, M> ingredientComponent, T instance, M matchCondition,\n                                boolean ignoreExistingJobs);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkCraftingHandlerRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.init.IRegistry;\n\nimport java.util.Collection;\n\n/**\n * Registry for {@link INetworkCraftingHandler}.\n * @author rubensworks\n */\npublic interface INetworkCraftingHandlerRegistry extends IRegistry {\n\n    /**\n     * Register a new crafting handler.\n     * @param craftingHandler The crafting handler.\n     * @param <C> The crafting handler type.\n     * @return The registered crafting handler.\n     */\n    public <C extends INetworkCraftingHandler> C register(C craftingHandler);\n\n    /**\n     * @return All registered crafting handlers.\n     */\n    public Collection<INetworkCraftingHandler> getCraftingHandlers();\n\n    /**\n     * Check if at least one crafting handler is crafting the given instance.\n     * @param network The network to craft in.\n     * @param ingredientsNetwork The ingredients network to craft in.\n     * @param channel A channel.\n     * @param ingredientComponent The ingredient component.\n     * @param instance The instance to craft.\n     * @param matchCondition The condition under which the instance should be crafted.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter, may be Void.\n     * @return If the instance is being crafted.\n     */\n    public default <T, M> boolean isCrafting(INetwork network, IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork, int channel,\n                                             IngredientComponent<T, M> ingredientComponent, T instance, M matchCondition) {\n        for (INetworkCraftingHandler craftingHandler : getCraftingHandlers()) {\n            if (craftingHandler.isCrafting(network, ingredientsNetwork, channel, ingredientComponent, instance, matchCondition)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check if at least one crafting handler is applicable for the given channel.\n     * @param network The network to craft in.\n     * @param ingredientsNetwork The ingredients network to craft in.\n     * @param channel A channel.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter, may be Void.\n     * @return If we can craft in the given channel.\n     */\n    public default <T, M> boolean canCraft(INetwork network, IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork, int channel) {\n        for (INetworkCraftingHandler craftingHandler : getCraftingHandlers()) {\n            if (craftingHandler.canCraft(network, ingredientsNetwork, channel)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Start a crafting job for the given instance in the given channel.\n     * @param network The network to craft in.\n     * @param ingredientsNetwork The ingredients network to craft in.\n     * @param channel A channel.\n     * @param ingredientComponent The ingredient component.\n     * @param instance The instance to craft.\n     * @param matchCondition The condition under which the instance should be crafted.\n     * @param ignoreExistingJobs If running jobs for the given instance should be ignored,\n     *                           and a new job should be started anyways.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter, may be Void.\n     * @return If a crafting job could be started.\n     */\n    public default <T, M> boolean craft(INetwork network, IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork, int channel,\n                                        IngredientComponent<T, M> ingredientComponent, T instance, M matchCondition, boolean ignoreExistingJobs) {\n        for (INetworkCraftingHandler craftingHandler : getCraftingHandlers()) {\n            if (craftingHandler.craft(network, ingredientsNetwork, channel, ingredientComponent, instance, matchCondition, ignoreExistingJobs)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.ScheduledTickAccess;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.Orientation;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.GeneralConfig;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * Objects that can be an element of a {@link INetwork}.\n * Multiple instances for the same 'element' can be created, so the comparator implementation must\n * make sure that these instances are considered equal.\n * These instances are used as a simple way of referring to these elements.\n * @author rubensworks\n */\npublic interface INetworkElement extends Comparable<INetworkElement> {\n\n    /**\n     * @return The tick interval to update this element.\n     */\n    public int getUpdateInterval();\n\n    /**\n     * @return If this element should be updated. This method is only called once during network initialization.\n     */\n    public boolean isUpdate();\n\n    /**\n     * Update at the tick interval specified.\n     * @param network The network to update in.\n     */\n    public void update(INetwork network);\n\n    /**\n     * Called right before the network is terminated or will be reset.\n     *\n     * @param network     The network to update in.\n     * @param blockState  The block state.\n     * @param blockEntity The block entity.\n     */\n    public void beforeNetworkKill(INetwork network, @Nullable BlockState blockState, @Nullable BlockEntity blockEntity);\n\n    /**\n     * Called right after this network is initialized.\n     * @param network The network to update in.\n     */\n    public void afterNetworkAlive(INetwork network);\n\n    /**\n     * Called right after this network has come alive again,\n     * for example after a network restart.\n     * @param network The network to update in.\n     */\n    public void afterNetworkReAlive(INetwork network);\n\n    /**\n     * Add the itemstacks to drop when this element is removed.\n     *\n     * @param blockState      The block state of the container block.\n     * @param blockEntity     The block entity of the container block.\n     * @param itemStacks      The itemstack list to add to.\n     * @param dropMainElement If the part itself should also be dropped.\n     * @param saveState       If the element state should be saved in the item.\n     */\n    public void addDrops(BlockState blockState, BlockEntity blockEntity, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState);\n\n    /**\n     * Called when this element is added to the network.\n     * @param network The network.\n     * @return If the addition succeeded.\n     */\n    public boolean onNetworkAddition(INetwork network);\n\n    /**\n     * Called when this element is removed from the network.\n     *\n     * @param network     The network.\n     * @param blockState  The block state.\n     * @param blockEntity The block entity.\n     */\n    public void onNetworkRemoval(INetwork network, BlockState blockState, BlockEntity blockEntity);\n\n    /**\n     * Called when this element is about to be removed.\n     * This is called before {@link INetwork#removeNetworkElementPre(INetworkElement)}.\n     * @param network The network.\n     */\n    public void onPreRemoved(INetwork network);\n\n    /**\n     * Called when this element has been removed.\n     * This is called after {@link IFullNetworkListener#removeNetworkElementPost(INetworkElement, BlockState, BlockEntity)}.\n     * @param network The network.\n     */\n    public void onPostRemoved(INetwork network);\n\n    /**\n     * Called when a neighbouring block is updated, more specifically when\n     * {@link Block#neighborChanged(BlockState, Level, BlockPos, Block, Orientation, boolean)},\n     * {@link Block#onNeighborChange(BlockState, LevelReader, BlockPos, BlockPos)}\n     * or {@link Block#updateShape(BlockState, LevelReader, ScheduledTickAccess, BlockPos, Direction, BlockPos, BlockState, RandomSource)} is called.\n     *\n     * @param network The network to update in.\n     * @param world   The world in which the neighbour was updated.\n     * @param side    The side at the center block.\n     */\n    public void onNeighborBlockChange(@Nullable INetwork network, BlockGetter world, @Nullable Direction side);\n\n    /**\n     * Set the priority and channel of this element in the network.\n     * @deprecated Should only be called from {@link INetwork#setPriorityAndChannel(INetworkElement, int, int)}!\n     * @param network The network this element is present in.\n     * @param priority The new priority\n     * @param channel The new channel\n     */\n    @Deprecated\n    public void setPriorityAndChannel(INetwork network, int priority, int channel);\n\n    /**\n     * @return The priority of this element in the network.\n     */\n    public int getPriority();\n\n    /**\n     * @return The channel of this element in the network.\n     */\n    public int getChannel();\n\n    /**\n     * Invalidate this network element.\n     * @param network The network.\n     */\n    public void invalidate(INetwork network);\n    /**\n     * Check if this element can be revalidated if it has been invalidated.\n     * @param network The network.\n     * @return If it can be revalidated.\n     */\n    public boolean canRevalidate(INetwork network);\n    /**\n     * Revalidate this network element after it has been invalidated.\n     * @param network The network.\n     */\n    public void revalidate(INetwork network);\n\n    /**\n     * @return If this element's position is currently loaded in the world.\n     */\n    public boolean isLoaded();\n\n    /**\n     * If a network element on the given position should tick.\n     * This can be used as implementation for {@link INetworkElement#isLoaded()}.\n     * @param pos A position.\n     * @return If it should tick.\n     */\n    public static boolean shouldTick(DimPos pos) {\n        return pos.isLoaded() && (GeneralConfig.tickUnloadedNetworkElements || pos.getLevel(true).shouldTickBlocksAt(pos.getBlockPos()));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkElementProvider.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.Level;\n\nimport java.util.Collection;\n\n/**\n * Capability that can create instances of an {@link INetworkElement}.\n * Blocks that provide this capability MUST properly call\n * {@link org.cyclops.integrateddynamics.core.helper.NetworkHelpers#onElementProviderBlockNeighborChange(Level, BlockPos, Direction)}.\n * @author rubensworks\n */\npublic interface INetworkElementProvider {\n\n    /**\n     * Create network element instances for the given position.\n     * @param world The world.\n     * @param blockPos The position.\n     * @return A collection of all network elements at this position.\n     */\n    public Collection<INetworkElement> createNetworkElements(Level world, BlockPos blockPos);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkEventListener.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\n\nimport java.util.Set;\n\n/**\n * Interface to indicate delegates of a network element instance.\n * @author rubensworks\n */\npublic interface INetworkEventListener<E> {\n\n    /**\n     * @return If this should be registered to the network event bus for listening to network events.\n     */\n    public boolean hasEventSubscriptions();\n\n    /**\n     * @return The static set of events this listener should be subscribed to.\n     */\n    public Set<Class<? extends INetworkEvent>> getSubscribedEvents();\n\n    /**\n     * Can be called at any time by the {@link org.cyclops.integrateddynamics.api.network.event.INetworkEventBus}.\n     * Only events in the set from {@link INetworkEventListener#getSubscribedEvents()} will be received.\n     * @param event The received event.\n     * @param networkElement The network element.\n     */\n    public void onEvent(INetworkEvent event, E networkElement);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/INetworkIngredientsChannel.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport javax.annotation.Nonnull;\n\n/**\n * @author rubensworks\n */\npublic interface INetworkIngredientsChannel<T, M> extends IIngredientComponentStorage<T, M> {\n\n    public Iterable<PartPos> findNonFullPositions();\n    public Iterable<PartPos> findAllPositions();\n    public Iterable<PartPos> findNonEmptyPositions();\n    public Iterable<PartPos> findMatchingPositions(@Nonnull T prototype, M matchFlags);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IPartNetwork.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.ILazyExpressionValueCache;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\n\n/**\n * A network capability that can hold parts.\n * Note that this network only contains references to the relevant data, it does not contain the actual information.\n * @author rubensworks\n */\npublic interface IPartNetwork extends ILazyExpressionValueCache {\n\n    /**\n     * Add the given part state to the network.\n     * @param partId The id of the part.\n     * @param partPos The part position to add.\n     * @return If the addition was successful.\n     */\n    public boolean addPart(int partId, PartPos partPos);\n\n    /**\n     * Get the part state by id from this network.\n     * @param partId The part state id.\n     * @return The corresponding part state or null.\n     */\n    public IPartState getPartState(int partId);\n\n    /**\n     * Get the part by id from this network.\n     * @param partId The part state id.\n     * @return The corresponding part or null.\n     */\n    public IPartType getPartType(int partId);\n\n    /**\n     * Remove the part state by id from this network.\n     * @param partId The part state id.\n     */\n    public void removePart(int partId);\n\n    /**\n     * Check if this network contains the given part id.\n     * @param partId The part state id.\n     * @return If this part is present in this network.\n     */\n    public boolean hasPart(int partId);\n\n    /**\n     * Check if a variable can be found for a given part and aspect.\n     * @param partId The part state id.\n     * @param aspect The aspect from the given part.\n     * @param <V> The value.\n     * @return True if such a variable can be found. False if the given part is not present in the network or if the\n     *         given aspect is not present at that part.\n     */\n    public <V extends IValue> boolean hasPartVariable(int partId, IAspectRead<V, ?> aspect);\n\n    /**\n     * Get the current variable from the aspect of the given part id.\n     * This method can call a NPE or cast exception when the given part does not exists, so make sure to check this before.\n     * @param partId The part state id.\n     * @param aspect The aspect from the given part.\n     * @param <V> The value.\n     * @return The variable.\n     */\n    public <V extends IValue> IVariable<V> getPartVariable(int partId, IAspectRead<V, ?> aspect);\n\n    /**\n     * Check if this network has access to the variable facade with given variable id.\n     * @param variableId The variable id.\n     * @return If this network has access to it.\n     */\n    public boolean hasVariableFacade(int variableId);\n\n    /**\n     * Get the variable facade with given variable id.\n     * @param variableId The variable id.\n     * @return The variable facade.\n     */\n    public IVariableFacade getVariableFacade(int variableId);\n\n    /**\n     * Add the position of a variable container.\n     * @param dimPos The variable container position.\n     * @return If the container did not exist in the network already.\n     */\n    public boolean addVariableContainer(DimPos dimPos);\n\n    /**\n     * Remove the position of a variable container.\n     * @param dimPos The variable container position.\n     */\n    public void removeVariableContainer(DimPos dimPos);\n\n    /**\n     * Add the given proxy to the network.\n     * @param proxyId The id of the proxy.\n     * @param dimPos The proxy position.\n     * @return If the addition was successful.\n     */\n    public boolean addProxy(int proxyId, DimPos dimPos);\n\n    /**\n     * Remove the proxy by id from this network.\n     * @param proxyId The id of the proxy.\n     */\n    public void removeProxy(int proxyId);\n\n    /**\n     * Check if this network contains the given part id.\n     * @param proxyId The id of the proxy.\n     * @return The proxy position.\n     */\n    public DimPos getProxy(int proxyId);\n\n    /**\n     * Tell the network to recheck all parts next update round.\n     */\n    public void notifyPartsChanged();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IPartNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * A part network element.\n * @author rubensworks\n */\npublic interface IPartNetworkElement<P extends IPartType<P, S>, S extends IPartState<P>> extends\n        IEventListenableNetworkElement<P>, IPositionedNetworkElement, ISidedNetworkElement, IIdentifiableNetworkElement {\n\n    public static final Identifier GROUP = Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"part\");\n\n    /**\n     * @return The part.\n     */\n    public P getPart();\n\n    /**\n     * @return The state for this part.\n     * @throws PartStateException If the part state could not be found.\n     */\n    public S getPartState() throws PartStateException;\n\n    /**\n     * @return The container in which this part resides.\n     */\n    public IPartContainer getPartContainer();\n\n    /**\n     * @return The target and position of this part.\n     */\n    public PartTarget getTarget();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IPartPosIteratorHandler.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport java.util.Iterator;\nimport java.util.function.Supplier;\n\n/**\n * Determines the starting position of an iterator of {@link PartPos}.\n * @author rubensworks\n */\npublic interface IPartPosIteratorHandler {\n\n    /**\n     * Given an iteraror constructor, determine the starting position of it.\n     * @param iteratorSupplier A constructor of an iterator of {@link PartPos}.\n     *                         Each invocation must produce a new iterator.\n     * @param channel The channel in which the iterator is being handled.\n     * @return An iterator, may be ended.\n     */\n    public Iterator<PartPos> handleIterator(Supplier<Iterator<PartPos>> iteratorSupplier, int channel);\n\n    /**\n     * Copy this handler.\n     * This should be cheap to call, as this can be called multiple times per tick.\n     * @return Create a stateful copy of this handler.\n     */\n    public IPartPosIteratorHandler clone();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IPositionedAddonsNetwork.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport com.google.common.collect.Iterables;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.stream.Collectors;\n\n/**\n * A network that can hold prioritized positions.\n * @author rubensworks\n */\npublic interface IPositionedAddonsNetwork {\n\n    /**\n     * The channel that should be used for anything that doesn't allow the player to select a channel.\n     */\n    public static final int DEFAULT_CHANNEL = 0;\n\n    /**\n     * Specifying this channel will allow interaction with all channels.\n     */\n    public static final int WILDCARD_CHANNEL = -1;\n\n    /**\n     * Whether two parts on the given channels may interact.\n     * @param first The id of the first channel.\n     * @param second The id of the second channel.\n     * @return If the two channels match.\n     */\n    public static boolean channelsMatch(int first, int second) {\n        return first == second || first == WILDCARD_CHANNEL || second == WILDCARD_CHANNEL;\n    }\n\n    /**\n     * @return The channels that have at least one active position.\n     */\n    public int[] getChannels();\n\n    /**\n     * @return If any positioned addons are present in this network.\n     */\n    public boolean hasPositions();\n\n    /**\n     * @param channel The channel id.\n     * @return The stored positions, sorted by priority.\n     */\n    public Collection<PrioritizedPartPos> getPrioritizedPositions(int channel);\n\n    /**\n     * Get the channel this position is present in.\n     * -1 if it is not present in any channel.\n     * @param pos A position.\n     * @return A channel.\n     */\n    public int getPositionChannel(PartPos pos);\n\n    /**\n     * @param channel The channel id.\n     * @return The stored positions, sorted by priority.\n     */\n    public default Collection<PartPos> getPositions(int channel) {\n        return getPrioritizedPositions(channel).stream().map(PrioritizedPartPos::getPartPos).collect(Collectors.toList());\n    }\n\n    /**\n     * @return All stored positions, order is undefined.\n     */\n    public Collection<PrioritizedPartPos> getPrioritizedPositions();\n\n    /**\n     * @return All stored positions, order is undefined.\n     */\n    public default Iterable<PartPos> getPositions() {\n        return Iterables.transform(getPrioritizedPositions(), PrioritizedPartPos::getPartPos);\n    }\n\n    /**\n     * @return The part positions iterator handler for this network.\n     */\n    @Nullable\n    public IPartPosIteratorHandler getPartPosIteratorHandler();\n\n    /**\n     * Set a part positions iterator handler for this network.\n     * @param iteratorHandler An iterator handler or null if it should be reset.\n     */\n    public void setPartPosIteratorHandler(@Nullable IPartPosIteratorHandler iteratorHandler);\n\n    /**\n     * Add the given position.\n     * @param pos The position.\n     * @param priority The priority.\n     * @param channel The channel id.\n     * @return If the position was added, otherwise it was already present.\n     */\n    public boolean addPosition(PartPos pos, int priority, int channel);\n\n    /**\n     * Remove the given position.\n     * @param pos The position.\n     */\n    public void removePosition(PartPos pos);\n\n    /**\n     * Check if the given position is disabled.\n     * @param pos The position.\n     * @return If it is disabled.\n     */\n    public boolean isPositionDisabled(PartPos pos);\n\n    /**\n     * Disable a position.\n     * @param pos The position.\n     */\n    public void disablePosition(PartPos pos);\n\n    /**\n     * Enable a position.\n     * @param pos The position.\n     */\n    public void enablePosition(PartPos pos);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IPositionedAddonsNetworkIngredients.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport com.google.common.collect.Iterators;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.neoforged.neoforge.capabilities.BlockCapability;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageSlotted;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageWrapperHandler;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentStorageObservable;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.network.IIngredientChannelInsertPreConsumer;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * An ingredient network that can hold prioritized positions.\n * @param <T> The instance type.\n * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n * @author rubensworks\n */\npublic interface IPositionedAddonsNetworkIngredients<T, M> extends IPositionedAddonsNetwork,\n        IIngredientComponentStorageObservable<T, M> {\n\n    /**\n     * @return The ingredient component type this storage applies to.\n     */\n    public IngredientComponent<T, M> getComponent();\n\n    /**\n     * @return The quantity rate limit for each storage mutation.\n     */\n    public long getRateLimit();\n\n    /**\n     * Get the storage at the given position.\n     * @param pos A position.\n     * @return The storage, or an empty storage if none is available.\n     */\n    public IIngredientComponentStorage<T, M> getPositionedStorage(PartPos pos);\n\n    /**\n     * Set an ingredient filter for the given storage position.\n     * Unsets the filter if null is provided.\n     * @param pos A position.\n     * @param filter An ingredient filter.\n     */\n    public void setPositionedStorageFilter(PartPos pos, @Nullable PositionedAddonsNetworkIngredientsFilter<T> filter);\n\n    /**\n     * @param pos A position.\n     * @return An optional ingredient filter for the given storage position.\n     */\n    @Nullable\n    public PositionedAddonsNetworkIngredientsFilter<T> getPositionedStorageFilter(PartPos pos);\n\n    /**\n     * Get all instances at the target position.\n     * @param pos A part position.\n     * @return A collection of instances. This can not be a view, and must be a deep copy of the target.\n     */\n    public default Iterator<T> getRawInstances(PartPos pos) {\n        Iterator<T> it = getPositionedStorage(pos).iterator();\n        PositionedAddonsNetworkIngredientsFilter<T> filter = getPositionedStorageFilter(pos);\n        if (filter != null) {\n            it = Iterators.filter(it, filter::testView);\n        }\n        return it;\n    }\n\n    /**\n     * Get the storage at the given position.\n     * @param pos A position.\n     * @return The storage.\n     */\n    @Nullable\n    public default IIngredientComponentStorage<T, M> getPositionedStorageUnsafe(PartPos pos) {\n        DimPos dimPos = pos.getPos();\n        Level level = dimPos.getLevel(true);\n        if (level == null) {\n            return null;\n        }\n        return getComponent().getBlockStorage(level, pos.getPos().getBlockPos(), level.getBlockState(dimPos.getBlockPos()), IModHelpers.get().getBlockEntityHelpers().get(level, dimPos.getBlockPos(), BlockEntity.class).orElse(null), pos.getSide());\n    }\n\n    /**\n     * Get the storage at the given channel.\n     * @param channel A channel id.\n     * @return A storage.\n     */\n    public INetworkIngredientsChannel<T, M> getChannel(int channel);\n\n    /**\n     * Get the slotted storage at the given channel.\n     * @param channel A channel id.\n     * @return A slotted storage.\n     */\n    public IIngredientComponentStorageSlotted<T, M> getChannelSlotted(int channel);\n\n    /**\n     * Get the external storage at the given channel.\n     * @param capability A capability to wrap the channel in.\n     * @param channel A channel id.\n     * @param <S> The external storage type.\n     * @return An external storage, or null if no wrapping is possible for the given capability.\n     */\n    @Nullable\n    public default <S, C> S getChannelExternal(BlockCapability<S, C> capability, int channel) {\n        IIngredientComponentStorageWrapperHandler<T, M, S, C> wrapperHandler = getComponent()\n                .getStorageWrapperHandler(capability);\n        return wrapperHandler != null ? wrapperHandler.wrapStorage(getChannel(channel)) : null;\n    }\n\n    /**\n     * Get the last tick duration of the index observer.\n     * @return Duration in nanoseconds\n     */\n    public Map<PartPos, Long> getLastSecondDurationIndex();\n\n    /**\n     * Reset the last second duration count.\n     */\n    public void resetLastSecondDurationsIndex();\n\n    /**\n     * Register an insert pre-consumer.\n     * @param preConsumer The consumer.\n     */\n    public void registerInsertPreConsumer(IIngredientChannelInsertPreConsumer<T> preConsumer);\n\n    /**\n     * Unregister an insert pre-consumer.\n     * @param preConsumer The consumer.\n     */\n    public void unregisterInsertPreConsumer(IIngredientChannelInsertPreConsumer<T> preConsumer);\n\n    /**\n     * @return All registered insert pre-consumers.\n     */\n    public Collection<IIngredientChannelInsertPreConsumer<T>> getInsertPreConsumers();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/IPositionedNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\n\n/**\n * A network element that exists at a certain position.\n * @author rubensworks\n */\npublic interface IPositionedNetworkElement extends INetworkElement {\n\n    public DimPos getPosition();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/ISidedNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.core.Direction;\n\n/**\n * A network element that exists at a certain side.\n * @author rubensworks\n */\npublic interface ISidedNetworkElement extends INetworkElement {\n\n    public Direction getSide();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/NetworkCapability.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.neoforge.capabilities.BaseCapability;\nimport net.neoforged.neoforge.capabilities.CapabilityRegistry;\nimport net.neoforged.neoforge.capabilities.ICapabilityProvider;\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class NetworkCapability<T> extends BaseCapability<T, Void> {\n\n    public static <T> NetworkCapability<T> create(Identifier name, Class<T> typeClass) {\n        return (NetworkCapability<T>) registry.create(name, typeClass, void.class);\n    }\n\n    public static synchronized List<NetworkCapability<?>> getAll() {\n        return registry.getAll();\n    }\n\n    // INTERNAL\n\n    // Requires explicitly-typed constructor due to ECJ inference failure.\n    private static final CapabilityRegistry<NetworkCapability<?>> registry = new CapabilityRegistry<NetworkCapability<?>>((name, type, clazz) -> new NetworkCapability<>(name, type));\n\n    private NetworkCapability(Identifier name, Class<T> typeClass) {\n        super(name, typeClass, void.class);\n    }\n\n    @ApiStatus.Internal\n    @Nullable\n    public T getCapability(Map<NetworkCapability<?>, List<ICapabilityProvider<INetwork, Void, ?>>> providers, INetwork network) {\n        List<ICapabilityProvider<INetwork, Void, ?>> list = providers.get(this);\n        if (list != null) {\n            for (var provider : (List<ICapabilityProvider<INetwork, Void, T>>) (List) list) {\n                var ret = provider.getCapability(network, null);\n                if (ret != null)\n                    return ret;\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/PositionedAddonsNetworkIngredientsFilter.java",
    "content": "package org.cyclops.integrateddynamics.api.network;\n\nimport java.util.function.Predicate;\n\n/**\n * A data object for filter predicates for ingredient networks with their settings.\n * @author rubensworks\n */\npublic class PositionedAddonsNetworkIngredientsFilter<T> {\n\n    private final Predicate<T> filter;\n    private final boolean applyToInsertions;\n    private final boolean applyToExtractions;\n    private final boolean allowAllIfFilterNotApplied;\n\n    public PositionedAddonsNetworkIngredientsFilter(Predicate<T> filter, boolean applyToInsertions,\n                                                    boolean applyToExtractions, boolean allowAllIfFilterNotApplied) {\n        this.filter = filter;\n        this.applyToInsertions = applyToInsertions;\n        this.applyToExtractions = applyToExtractions;\n        this.allowAllIfFilterNotApplied = allowAllIfFilterNotApplied;\n    }\n\n    /**\n     * Test the given ingredient for an insertion operation.\n     * @param ingredient An ingredient to test.\n     * @return If the operation is allowed.\n     */\n    public boolean testInsertion(T ingredient) {\n        if (this.isApplyToInsertions()) {\n            return getFilter().test(ingredient);\n        } else {\n            return this.isAllowAllIfFilterNotApplied();\n        }\n    }\n\n    /**\n     * Test the given ingredient for an insertion operation.\n     * @param ingredient An ingredient to test.\n     * @return If the operation is allowed.\n     */\n    public boolean testExtraction(T ingredient) {\n        if (this.isApplyToExtractions()) {\n            return getFilter().test(ingredient);\n        } else {\n            return this.isAllowAllIfFilterNotApplied();\n        }\n    }\n\n    /**\n     * Test the given ingredient for read-only and iteration operations.\n     * @param ingredient An ingredient to test.\n     * @return If the operation is allowed.\n     */\n    public boolean testView(T ingredient) {\n        return this.testExtraction(ingredient);\n    }\n\n    /**\n     * @return The ingredient filter\n     */\n    public Predicate<T> getFilter() {\n        return filter;\n    }\n\n    /**\n     * @return If the ingredient filter should be used for insertions.\n     */\n    public boolean isApplyToInsertions() {\n        return applyToInsertions;\n    }\n\n    /**\n     * @return If the ingredient filter should be used for extractions.\n     */\n    public boolean isApplyToExtractions() {\n        return applyToExtractions;\n    }\n\n    /**\n     * @return Determines what to do if insertion or extraction application is disabled.\n     *         If this method returns true, then all insertions and/or extractions will be allowed when their respective application is disabled.\n     *         If this method returns false, then no insertions and/or extractions will be allowed when their respective application is disabled.\n     */\n    public boolean isAllowAllIfFilterNotApplied() {\n        return allowAllIfFilterNotApplied;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/event/ICancelableNetworkEvent.java",
    "content": "package org.cyclops.integrateddynamics.api.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\n\n/**\n * An event posted in the {@link INetwork} event bus.\n * @author rubensworks\n */\npublic interface ICancelableNetworkEvent extends INetworkEvent {\n\n    /**\n     * Cancel this event from further processing.\n     */\n    public void cancel();\n\n    /**\n     * @return If this event was canceled.\n     */\n    public boolean isCanceled();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/event/INetworkEvent.java",
    "content": "package org.cyclops.integrateddynamics.api.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\n\n/**\n * An event posted in the {@link INetwork} event bus.\n * @author rubensworks\n */\npublic interface INetworkEvent {\n\n    /**\n     * @return The network this event is thrown in.\n     */\n    public INetwork getNetwork();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/network/event/INetworkEventBus.java",
    "content": "package org.cyclops.integrateddynamics.api.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.IEventListenableNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\n\n/**\n * An event bus for {@link INetwork} events where\n * {@link INetworkElement} instances can listen to.\n * @author rubensworks\n */\npublic interface INetworkEventBus {\n\n    /**\n     * Register a network element for the given event type.\n     * @param target The element that will be called once the event bus receives the given event.\n     * @param eventType The event type.\n     */\n    public void register(IEventListenableNetworkElement<?> target, Class<? extends INetworkEvent> eventType);\n\n    /**\n     * Unregister a network element for the given event type.\n     * @param target The element that would be called once the event bus receives the given event.\n     * @param eventType The event type.\n     */\n    public void unregister(IEventListenableNetworkElement<?> target, Class<? extends INetworkEvent> eventType);\n\n    /**\n     * Unregister all events for the given network element.\n     * @param target The element that would be called once the event bus receives events.\n     */\n    public void unregister(IEventListenableNetworkElement<?> target);\n\n    /**\n     * Post the given event to the events bus.\n     * @param event The event to post.\n     */\n    public void post(INetworkEvent event);\n\n    /**\n     * Post the given cancelable event to the events bus.\n     * @param event The event to post.\n     * @return If the event was not canceled.\n     */\n    public boolean postCancelable(ICancelableNetworkEvent event);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/AttachCapabilitiesEventPart.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.neoforged.bus.api.Event;\nimport net.neoforged.neoforge.capabilities.ICapabilityProvider;\n\nimport java.util.ArrayList;\nimport java.util.Objects;\n\n/**\n * Event for when an {@link IPartState} is being constructed.\n * @author rubensworks\n */\npublic class AttachCapabilitiesEventPart extends Event {\n\n    private final IPartType partType;\n    private final IPartState partState;\n\n    public AttachCapabilitiesEventPart(IPartType partType, IPartState partState) {\n        this.partType = partType;\n        this.partState = partState;\n    }\n\n    public IPartType getPartType() {\n        return partType;\n    }\n\n    public IPartState getPartState() {\n        return this.partState;\n    }\n\n    public <T> void register(\n            PartCapability<T> capability,\n            IPartType<?, ?> partType,\n            ICapabilityProvider<IPartType<?, ?>, PartTarget, T> provider\n    ) {\n        Objects.requireNonNull(provider);\n        capability.providers.computeIfAbsent(Objects.requireNonNull(partType), i -> new ArrayList<>()).add(provider);\n    }\n\n    public boolean isRegistered(PartCapability<?> capability, IPartType<?, ?> partType) {\n        Objects.requireNonNull(partType);\n        return capability.providers.containsKey(partType);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/IPartContainer.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.persist.nbt.INBTSerializable;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * An interface for containers that can hold {@link IPartType}s.\n * @author rubensworks\n */\npublic interface IPartContainer extends INBTSerializable {\n\n    /**\n     * Should be called every tick, updates parts.\n     */\n    public void update();\n\n    /**\n     * @return The position this container is at.\n     */\n    public DimPos getPosition();\n\n    /**\n     * @return The parts inside this container.\n     */\n    public Map<Direction, IPartType<?, ?>> getParts();\n\n    /**\n     * @return If this container has at least one part.\n     */\n    public boolean hasParts();\n\n    /**\n     * Set the part for a side.\n     * @param side The side to place the part on.\n     * @param part The part.\n     * @param partState The state for this part.\n     * @param <P> The type of part.\n     * @param <S> The type of part state.\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void setPart(Direction side, IPartType<P, S> part,\n                                                                             IPartState<P> partState);\n\n    /**\n     * Check if the given part can be added at the given side.\n     * @param side The side to place the part on.\n     * @param part The part.\n     * @param <P> The type of part.\n     * @param <S> The type of part state.\n     * @return If the part can be added.\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> boolean canAddPart(Direction side, IPartType<P, S> part);\n\n    /**\n     * Get the part of a side, can be null.\n     * @param side The side.\n     * @return The part or null.\n     */\n    public IPartType getPart(Direction side);\n\n    /**\n     * @param side The side.\n     * @return If the given side has a part.\n     */\n    public boolean hasPart(Direction side);\n\n    /**\n     * Remove the part from a side, can return null if there was no part on that side.\n     * @param side The side.\n     * @param player The player removing the part.\n     * @param dropMainElement If the main part element should be dropped.\n     * @param saveState If the part state should be saved in the item.\n     * @return The removed part or null.\n     */\n    public IPartType removePart(Direction side, @Nullable Player player, boolean dropMainElement, boolean saveState);\n\n    /**dz\n     * Set the state of a part.\n     * @param side The side.\n     * @param partState The part state.\n     * @throws PartStateException If no part at the given position is available.\n     */\n    public void setPartState(Direction side, IPartState partState) throws PartStateException;\n\n    /**\n     * Get the state of a part.\n     * @param side The side.\n     * @return The part state.\n     * @throws PartStateException If no part at the given position is available.\n     */\n    public IPartState getPartState(Direction side) throws PartStateException;\n\n    /**\n     * Get the part side the player is watching.\n     * This is used to determine the part the player is looking at.\n     * @param world The world.\n     * @param pos The block position to perform a ray trace for.\n     * @param player The player.\n     * @return The side the player is watching or null.\n     */\n    public @Nullable\n    Direction getWatchingSide(Level world, BlockPos pos, Player player);\n\n    /**\n     * Get a part capability.\n     * @param partCapability A part capability.\n     * @param network The network.\n     * @param partNetwork The part network.\n     * @param target The part target.\n     * @param <T> The capability type.\n     * @return The capability instance.\n     */\n    public <T> Optional<T> getCapability(PartCapability<T> partCapability, INetwork network, IPartNetwork partNetwork, PartTarget target);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/IPartState.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * A value holder for an {@link IPartType}.\n * This is what will be serialized from and to NBT.\n * This object is mutable and should not be recreated.\n * Note that you should be careful when passing around this state, because when the server sends an update to the\n * client, this state could be overwritten with a new version, so always try to use the part container to get the state.\n * @author rubensworks\n */\npublic interface IPartState<P extends IPartType> {\n\n    public static final String GLOBALCOUNTER_KEY = \"part\";\n\n    /**\n     * Write a state to NBT.\n     *\n     * @param valueOutput The value to write to.\n     */\n    public void serialize(ValueOutput valueOutput);\n\n    /**\n     * Read a state from NBT.\n     *\n     * @param valueInput The value to read from.\n     */\n    public void deserialize(ValueInput valueInput);\n\n    /**\n     * Generate a server-wide unique ID for this part state.\n     */\n    public void generateId();\n\n    /**\n     * A server-wide unique ID for this part that is persisted when the part is broken and moved.\n     * @return The unique ID\n     */\n    public int getId();\n\n    /**\n     * Set the update interval for this state.\n     * @param updateInterval The tick interval to update this element.\n     */\n    public void setUpdateInterval(int updateInterval);\n\n    /**\n     * @return The tick interval to update this element.\n     */\n    public int getUpdateInterval();\n\n    /**\n     * Set the priority of this part in the network.\n     * @deprecated Should only be called from {@link org.cyclops.integrateddynamics.api.network.INetwork#setPriorityAndChannel(INetworkElement, int, int)}}!\n     * @param priority The new priority\n     */\n    @Deprecated\n    public void setPriority(int priority);\n\n    /**\n     * @return The priority of this part in the network.\n     */\n    public int getPriority();\n\n    /**\n     * Set the channel for this state.\n     * @deprecated Should only be called from {@link org.cyclops.integrateddynamics.api.network.INetwork#setPriorityAndChannel(INetworkElement, int, int)}}!\n     * @param channel The new channel\n     */\n    @Deprecated\n    public void setChannel(int channel);\n\n    /**\n     * @return This part's channel.\n     */\n    public int getChannel();\n\n    /**\n     * @return The target position offset.\n     */\n    public Vec3i getTargetOffset();\n\n    /**\n     * @param offset The target position offset.\n     */\n    public void setTargetOffset(Vec3i offset);\n\n    /**\n     * Indicate that the given part should interact with the given side of the target.\n     * @param side The side of the target block to interact with.\n     *             Null removes the side override.\n     */\n    public void setTargetSideOverride(@Nullable Direction side);\n\n    /**\n     * @return The side of the target block to interact with. Can be null.\n     */\n    @Nullable\n    public Direction getTargetSideOverride();\n\n    /**\n     * Indicate that this state has changes that must be saved to the world.\n     */\n    public void markDirty();\n\n    /**\n     * Check if dirty and reset the dirty state.\n     * @return If this state has changed since the last time and needs to be persisted to NBT eventually.\n     */\n    public boolean isDirtyAndReset();\n\n    /**\n     * Check if this part state should update and reset the flag.\n     * @return If this state has changed since the last time and needs to be updated to the client.\n     */\n    public boolean isUpdateAndReset();\n\n    /**\n     * Set a flag indicating that the next time that\n     * {@link IPartType#shouldTriggerBlockRenderUpdate(IPartState, IPartState)}\n     * is queried, it should return true.\n     *\n     * This is useful in cases where the player makes changes inside a part,\n     * the state difference checking can not be relied upon,\n     * and a state update should be forced in any case.\n     *\n     * This should only be called client-side.\n     */\n    public void forceBlockRenderUpdate();\n\n    /**\n     * @return If a block render update is forced.\n     * This flagged will be set to false after this method is called.\n     *\n     * This should only be called client-side.\n     */\n    public boolean isForceBlockRenderUpdateAndReset();\n\n    /**\n     * Get the properties for the given aspect.\n     * This will only retrieve the already saved properties, so this could be null if not set before.\n     * It is better to call the {@link IAspect#getProperties(IPartType, PartTarget, IPartState)} method instead.\n     * @param aspect The aspect to get the properties from.\n     * @return The properties, this can be null if still the default.\n     */\n    public IAspectProperties getAspectProperties(IAspect aspect);\n\n    /**\n     * Set the properties for the given aspect.\n     * @param aspect The aspect to get the properties from.\n     * @param properties The properties, this can be null if still the default.\n     */\n    public void setAspectProperties(IAspect aspect, IAspectProperties properties);\n\n    /**\n     * Enable the part from working.\n     * @param enabled If it should work.\n     */\n    public void setEnabled(boolean enabled);\n\n    /**\n     * @return If the part should work.\n     */\n    public boolean isEnabled();\n\n    /**\n     * Gathers the capabilities of this part state.\n     * Don't call this unless you know what you're doing!\n     * @param partType The part type this state is associated with.\n     */\n    public void gatherCapabilities(P partType);\n\n    /**\n     * Get the given capability.\n     * @param <T> The capability type.\n     * @param partType The part type.\n     * @param capability The capability to get.\n     * @param network The network the part belongs to.\n     * @param partNetwork The part network the part belongs to.\n     * @param target The target.\n     * @return The optional capability instance.\n     */\n    public <T> Optional<T> getCapability(P partType, PartCapability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target);\n\n    /**\n     * Add a capability to this state that will not be automatically persisted to NBT.\n     * @param <T> The capability type.\n     * @param capability The optional capability.\n     * @param value The capability instance.\n     */\n    public <T> void addVolatileCapability(PartCapability<T> capability, Optional<T> value);\n\n    /**\n     * Remove a non-persisted capability.\n     * @param capability The capability.\n     */\n    public void removeVolatileCapability(PartCapability<?> capability);\n\n    /**\n     * Load the inventory of the given name from the part state.\n     * @param name The inventory name.\n     * @param inventory The inventory object to load into.\n     */\n    public default void loadInventoryNamed(String name, Container inventory) {\n        NonNullList<ItemStack> tabItems = this.getInventoryNamed(name);\n        if (tabItems != null) {\n            for (int i = 0; i < tabItems.size(); i++) {\n                inventory.setItem(i, tabItems.get(i));\n            }\n        }\n    }\n\n    /**\n     * Save the inventory of the given name into the part state.\n     * @param name The inventory name.\n     * @param inventory The inventory object to save.\n     */\n    public default void saveInventoryNamed(String name, Container inventory) {\n        NonNullList<ItemStack> latestItems = NonNullList.create();\n        for (int i = 0; i < inventory.getContainerSize(); i++) {\n            latestItems.add(inventory.getItem(i));\n        }\n        this.setInventoryNamed(name, latestItems);\n    }\n\n    /**\n     * @param name The inventory name.\n     * @return Get the inventory contents of the given name.\n     */\n    @Nullable\n    public NonNullList<ItemStack> getInventoryNamed(String name);\n\n    /**\n     * Set the inventory of the given name.\n     * @param name The inventory name.\n     * @param inventory Inventory contents.\n     */\n    public void setInventoryNamed(String name, NonNullList<ItemStack> inventory);\n\n    /**\n     * @return All named inventories.\n     */\n    public Map<String, NonNullList<ItemStack>> getInventoriesNamed();\n\n    /**\n     * Clear all named inventories.\n     */\n    public void clearInventoriesNamed();\n\n    /**\n     * Run the initialization logic for offset handling.\n     */\n    public void initializeOffsets(PartTarget target);\n\n    /**\n     * Tick any internal offset variables.\n     * @param partType The part type.\n     * @param network The network.\n     * @param partNetwork The part network.\n     * @param target The part target.\n     */\n    public void updateOffsetVariables(P partType, INetwork network, IPartNetwork partNetwork, PartTarget target);\n\n    /**\n     * Indicate that the contents of the offset variables inventory have changed.\n     */\n    public void markOffsetVariablesChanged();\n\n    /**\n     * @param slot The offset variable slot.\n     * @return The current error, or null if no error.\n     */\n    @Nullable\n    public MutableComponent getOffsetVariableError(int slot);\n\n    /**\n     * @return If the part contains variable-driven offsets that require updating.\n     */\n    public boolean requiresOffsetUpdates();\n\n    /**\n     * @return The max offset allowed in this part.\n     */\n    public int getMaxOffset();\n\n    /**\n     * Update the max offset for this part.\n     * @param offset The new offset.\n     */\n    public void setMaxOffset(int offset);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/IPartType.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.ScheduledTickAccess;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.*;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\n/**\n * A type of part that can be inserted into a {@link IPartContainer}.\n * Only one unique instance for each part should exist, the values are stored inside an\n * {@link IPartState}.\n * @param <P> The part type.\n * @param <S> The part state type.\n * @author rubensworks\n */\npublic interface IPartType<P extends IPartType<P, S>, S extends IPartState<P>> extends INetworkEventListener<IPartNetworkElement<P, S>> {\n\n    /**\n     * @return The unique name for this part type.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * @return The unlocalized name of this part.\n     */\n    public String getTranslationKey();\n\n    /**\n     * @return JSON facadeModel path for the block representation of this part.\n     */\n    public Identifier getBlockModelPath();\n\n    /**\n     * @return The item associated with this part type.\n     */\n    public Item getItem();\n\n    /**\n     * @param state The state\n     * @return If this element is solid.\n     */\n    public boolean isSolid(S state);\n\n    /**\n     * @return The position part occupies, used to calculate the required render lengths.\n     *         This part is assumed to be aligned at the edge of the block for the depth, while centered on width and height.\n     */\n    public PartRenderPosition getPartRenderPosition();\n\n    /**\n     * Write the properties of this part to NBT.\n     * An identificator for this part is not required, this is written somewhere else.\n     *\n     * @param valueOutput The value to write to.\n     * @param partState   The state of this part.\n     */\n    public void serializeState(ValueOutput valueOutput, S partState);\n\n    /**\n     * Read the properties of this part from nbt.\n     * This tag is guaranteed to only contain data for this part.\n     *\n     * @param valueInput The input to read from.\n     * @return The state of this part.\n     */\n    public S deserializeState(ValueInput valueInput);\n\n    /**\n     * @return The default state of this part.\n     */\n    public S defaultBlockState();\n\n    /**\n     * Set the update interval for this part.\n     * @param state The state\n     * @param updateInterval The tick interval to update this element.\n     */\n    public void setUpdateInterval(S state, int updateInterval);\n\n    /**\n     * @param state The state\n     * @return The tick interval to update this element.\n     */\n    public int getUpdateInterval(S state);\n\n    /**\n     * @param state The state\n     * @return The minimum allowed tick interval to update this element.\n     */\n    public int getMinimumUpdateInterval(S state);\n\n    /**\n     * Set the priority and channel of this part in the network.\n     * @deprecated Should only be called from {@link INetwork#setPriorityAndChannel(INetworkElement, int, int)}!\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     * @param priority The new priority\n     * @param channel The new channel\n     */\n    @Deprecated\n    public void setPriorityAndChannel(INetwork network, IPartNetwork partNetwork, PartTarget target, S state, int priority, int channel);\n\n    /**\n     * @param state The state\n     * @return The priority of this part in the network.\n     */\n    public int getPriority(S state);\n\n    /**\n     * @param state The state\n     * @return The channel of this part in the network.\n     */\n    public int getChannel(S state);\n\n    /**\n     * @return If this part can handle custom offsets.\n     */\n    public default boolean supportsOffsets() {\n        return true;\n    }\n\n    /**\n     * @param state The state\n     * @return The target position offset.\n     */\n    public Vec3i getTargetOffset(S state);\n\n    /**\n     * @param state The state\n     * @param center The center position.\n     * @param offset The target position offset.\n     * @return True if the offset was valid\n     */\n    public boolean setTargetOffset(S state, PartPos center, Vec3i offset);\n\n    /**\n     * Indicate that the given part should interact with the given side of the target.\n     * @param state The state\n     * @param side The side of the target block to interact with.\n     *             Null removes the side override.\n     */\n    public void setTargetSideOverride(S state, @Nullable Direction side);\n\n    /**\n     * @param state The state\n     * @return The overridden side of the target block to interact with. Can be null.\n     */\n    @Nullable\n    public Direction getTargetSideOverride(S state);\n\n    /**\n     * Get the part target for this part.\n     * @param pos The center position of this part.\n     * @param state The state.\n     * @return The part target.\n     */\n    public PartTarget getTarget(PartPos pos, S state);\n\n    /**\n     * Called when an offset variable was inserted or removed from a slot.\n     * @param target The target block.\n     * @param state The state.\n     */\n    public void onOffsetVariablesChanged(PartTarget target, S state);\n\n    /**\n     * @param state The state\n     * @return If this element should be updated. This method is only called once during network initialization.\n     */\n    public boolean isUpdate(S state);\n\n    /**\n     * Update at the tick interval specified.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Called right before the network is terminated or will be reset.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void beforeNetworkKill(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Called right after this network is initialized.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void afterNetworkAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Called right after this network has come alive again,\n     * for example after a network restart.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Get the itemstack from the given state.\n     *\n     * @param valueDeseralizationContext\n     * @param problemPath\n     * @param state                      The state\n     * @param saveState                  If the part state should be saved in the item.\n     * @return The itemstack possibly containing the state information.\n     */\n    public ItemStack getItemStack(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, S state, boolean saveState);\n\n    /**\n     * Get the itemstack from the given state.\n     * @param world The world.\n     * @param pos The position.\n     * @param state The state.\n     * @return The itemstack possibly containing the state information.\n     */\n    public ItemStack getCloneItemStack(Level world, BlockPos pos, S state);\n\n    /**\n     * Get the part state from the given itemstack.\n     *\n     * @param valueDeseralizationContext\n     * @param problemPath\n     * @param itemStack                  The itemstack possibly containing state information.\n     * @return The state contained in the itemstack or the default part state.\n     */\n    public S getState(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, ItemStack itemStack);\n\n    /**\n     * Add the itemstacks to drop when this element is removed.\n     * @param target The target.\n     * @param state The state\n     * @param itemStacks The itemstack list to add to.\n     * @param dropMainElement If the part itself should also be dropped.\n     * @param saveState If the part state should be saved in the item.\n     */\n    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState);\n\n    /**\n     * Called when this element is added to the network.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Called when this element is removed from the network.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Create a network element for this part type.\n     * @param partContainer The container this part is/will be part of.\n     * @param pos The position this network element is/will be placed at.\n     * @param side The side this network element is/will be placed at.\n     * @return A new network element instance.\n     */\n    public INetworkElement createNetworkElement(IPartContainer partContainer, DimPos pos, Direction side);\n\n    /**\n     * Called when a part is right-clicked.\n     * @param partState The state of this part.\n     * @param pos The position of the block this part is part of.\n     * @param world The world.\n     * @param player The player activating the part.\n     * @param hand The hand in use by the player.\n     * @param heldItem The held item.\n     * @param hit The ray trace hit result.\n     * @return The action result.\n     */\n    public InteractionResult onPartActivated(S partState, BlockPos pos, Level world, Player player, InteractionHand hand,\n                                            ItemStack heldItem, BlockHitResult hit);\n\n    /**\n     * Get the base block state that will be rendered for this part.\n     * An appropriate {@link org.cyclops.integrateddynamics.core.block.IgnoredBlock#FACING} property will be set.\n     * @param partContainer The part entity.\n     * @param side The position of the part.\n     * @return The block state to render with.\n     */\n    public BlockState getBlockState(IPartContainer partContainer, Direction side);\n\n    /**\n     * @return The default block state representation of this part.\n     */\n    public BlockState getBaseBlockState();\n\n    /**\n     * Called when a block update occurs\n     * @param world The world.\n     * @param pos The position.\n     * @param partState The part state.\n     * @param random A random instance.\n     */\n    public void updateTick(Level world, BlockPos pos, S partState, RandomSource random);\n\n    /**\n     * Called when this element is about to be removed.\n     * @param network The network.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void onPreRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Called after this element has been removed.\n     * @param network The network.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     */\n    public void onPostRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, S state);\n\n    /**\n     * Called when a neighbouring block is updated, more specifically when\n     * {@link Block#neighborChanged(BlockState, Level, BlockPos, Block, Orientation, boolean)},\n     * {@link Block#onNeighborChange(BlockState, LevelReader, BlockPos, BlockPos)}\n     * or {@link Block#updateShape(BlockState, LevelReader, ScheduledTickAccess, BlockPos, Direction, BlockPos, BlockState, RandomSource)} is called.\n     *\n     * @param network     The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target      The target block.\n     * @param state       The state\n     * @param world       The world in which the neighbour was updated.\n     * @param side        The side at the center block.\n     */\n    public void onBlockNeighborChange(@Nullable INetwork network, @Nullable IPartNetwork partNetwork, PartTarget target,\n                                              S state, BlockGetter world, @Nullable Direction side);\n\n    /**\n     * @param state The state\n     * @return The consumption rate of this part for the given state.\n     */\n    public int getConsumptionRate(S state);\n\n    /**\n     * Called after the element was updated or not.\n     * If the update was not called, this can be because the network did not contain\n     * enough energy to let this element work.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param target The target block.\n     * @param state The state\n     * @param updated If the {@link INetworkElement#update(INetwork)} was called.\n     */\n    public void postUpdate(INetwork network, IPartNetwork partNetwork, PartTarget target, S state, boolean updated);\n\n    /**\n     * @param state The state\n     * @return If this part is enabled.\n     */\n    public boolean isEnabled(S state);\n\n    /**\n     * Set if this part should work.\n     * @param state The state\n     * @param enabled If it should work.\n     */\n    public void setEnabled(S state, boolean enabled);\n\n    /**\n     * Add tooltip lines for this aspect when this part is being hovered by a mod like WAILA.\n     * @param state The state.\n     * @param lines The list to add lines to.\n     */\n    public void loadTooltip(S state, List<Component> lines);\n\n    /**\n     * Add tooltip lines for this aspect when this part's item is being hovered.\n     *\n     * @param itemStack    The itemstack.\n     * @param tooltipAdder The list to add lines to.\n     */\n    public void loadTooltip(ItemStack itemStack, Consumer<Component> tooltipAdder);\n\n    /**\n     * Check if the given state change should trigger a block render update.\n     * This is only called client-side.\n     * The new and old partstates are never both null, at most one will be null.\n     * @param oldPartState The old part state.\n     * @param newPartState The new part state.\n     * @return If it should trigger a block render update.\n     */\n    public boolean shouldTriggerBlockRenderUpdate(@Nullable S oldPartState, @Nullable S newPartState);\n\n    /**\n     * @param state The state.\n     * @return If this part should force the block to be transparent to light.\n     */\n    public boolean forceLightTransparency(S state);\n\n    /**\n     * {@link #writeExtraGuiData(RegistryFriendlyByteBuf, PartPos, ServerPlayer)}.\n     * @return The optional container provider for the part type gui.\n     * @param pos The part position. May be null when called client-side, for checking presence.\n     */\n    public default Optional<MenuProvider> getContainerProvider(PartPos pos) {\n        return Optional.empty();\n    };\n\n    /**\n     * This method can be overridden for cases when additional data needs to be sent to clients when opening containers.\n     * @param packetBuffer A packet buffer that can be written to.\n     * @param pos A part position.\n     * @param player The player opening the gui.\n     */\n    public default void writeExtraGuiData(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n\n    }\n\n    /**\n     * {@link #writeExtraGuiDataSettings(RegistryFriendlyByteBuf, PartPos, ServerPlayer)}.\n     * @return The optional container provider for the part settings gui.\n     * @param pos The part position. May be null when called client-side, for checking presence.\n     */\n    public default Optional<MenuProvider> getContainerProviderSettings(PartPos pos) {\n        return Optional.empty();\n    };\n\n    /**\n     * {@link #writeExtraGuiDataOffsets(RegistryFriendlyByteBuf, PartPos, ServerPlayer)}.\n     * @return The optional container provider for the part offsets gui.\n     * @param pos The part position. May be null when called client-side, for checking presence.\n     */\n    public default Optional<MenuProvider> getContainerProviderOffsets(PartPos pos) {\n        return Optional.empty();\n    };\n\n    /**\n     * This method can be overridden for cases when additional data needs to be sent to clients\n     * when opening settings containers.\n     * @param packetBuffer A packet buffer that can be written to.\n     * @param pos A part position.\n     * @param player The player opening the settings gui.\n     */\n    public default void writeExtraGuiDataSettings(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n\n    }\n\n    /**\n     * This method can be overridden for cases when additional data needs to be sent to clients\n     * when opening offsets containers.\n     * @param packetBuffer A packet buffer that can be written to.\n     * @param pos A part position.\n     * @param player The player opening the offsets gui.\n     */\n    public default void writeExtraGuiDataOffsets(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/IPartTypeActiveVariable.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\n\n/**\n * A part type that can have an active variable.\n * @author rubensworks\n */\npublic interface IPartTypeActiveVariable<P extends IPartTypeActiveVariable<P, S>, S extends IPartState<P>> extends IPartType<P, S> {\n\n    /**\n     * @param network The network this part belongs to.\n     * @param target The target block.\n     * @param partState The state of this part.\n     * @return If there is an active variable present.\n     */\n    public boolean hasActiveVariable(IPartNetwork network, PartTarget target, S partState);\n\n    /**\n     * Get the variable that is currently active for this part, the value in this variable will be used to write something.\n     * @param <V> The value type.\n     * @param network The network this part belongs to.\n     * @param partNetwork The part network this part belongs to.\n     * @param target The target block.\n     * @param partState The state of this part.\n     * @return The variable reference to some other value that needs to be written by this part.\n     */\n    public <V extends IValue> IVariable<V> getActiveVariable(INetwork network, IPartNetwork partNetwork, PartTarget target, S partState);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/IPartTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.init.IRegistry;\n\nimport java.util.Collection;\n\n/**\n * Registry for {@link IPartType}.\n * @author rubensworks\n */\npublic interface IPartTypeRegistry extends IRegistry {\n\n    /**\n     * Register a new part type.\n     * @param partType The part type.\n     * @param <P> The part type.\n     * @param <S> The state type.\n     * @return The registered part type.\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> P register(P partType);\n\n    /**\n     * @return All registered part types.\n     */\n    public Collection<IPartType> getPartTypes();\n\n    /**\n     * Get the part type by unique name.\n     * @param partName The unique part type name.\n     * @return The associated part type or null.\n     */\n    public IPartType getPartType(Identifier partName);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PartCapability.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.neoforge.capabilities.BaseCapability;\nimport net.neoforged.neoforge.capabilities.CapabilityRegistry;\nimport net.neoforged.neoforge.capabilities.ICapabilityProvider;\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class PartCapability<T> extends BaseCapability<T, PartTarget> {\n\n    public static <T> PartCapability<T> create(Identifier name, Class<T> typeClass) {\n        return (PartCapability<T>) registry.create(name, typeClass, PartTarget.class);\n    }\n\n    public static synchronized List<PartCapability<?>> getAll() {\n        return registry.getAll();\n    }\n\n    // INTERNAL\n\n    // Requires explicitly-typed constructor due to ECJ inference failure.\n    private static final CapabilityRegistry<PartCapability<?>> registry = new CapabilityRegistry<PartCapability<?>>((name, type, clazz) -> new PartCapability<>(name, type));\n\n    private PartCapability(Identifier name, Class<T> typeClass) {\n        super(name, typeClass, PartTarget.class);\n    }\n\n    final Map<IPartType<?, ?>, List<ICapabilityProvider<IPartType<?, ?>, PartTarget, T>>> providers = new IdentityHashMap<>();\n\n    @ApiStatus.Internal\n    @Nullable\n    public T getCapability(IPartType<?, ?> partType, PartTarget context) {\n        for (var provider : providers.getOrDefault(partType, List.of())) {\n            var ret = provider.getCapability(partType, context);\n            if (ret != null)\n                return ret;\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PartPathElement.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.util.ProblemReporter;\n\n/**\n * @author rubensworks\n */\npublic record PartPathElement(BlockPos pos) implements ProblemReporter.PathElement {\n    @Override\n    public String get() {\n        return \"part@\" + this.pos;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PartPos.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.world.level.Level;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport javax.annotation.Nullable;\n\n/**\n * Object holder to refer to a block side and position.\n * @author rubensworks\n */\npublic class PartPos implements Comparable<PartPos> {\n\n    static {\n        PacketCodecs.addCodedAction(PartPos.class, new PacketCodec.ICodecAction() {\n\n            @Override\n            public void encode(Object object, RegistryFriendlyByteBuf packetBuffer) {\n                PacketCodecs.write(packetBuffer, ((PartPos) object).getPos());\n                PacketCodecs.write(packetBuffer, ((PartPos) object).getSide());\n            }\n\n            @Override\n            public Object decode(RegistryFriendlyByteBuf packetBuffer) {\n                DimPos pos = PacketCodecs.read(packetBuffer, DimPos.class);\n                Direction side = PacketCodecs.read(packetBuffer, Direction.class);\n                return PartPos.of(pos, side);\n            }\n        });\n    }\n\n    private final DimPos pos;\n    private final Direction side;\n\n    public static PartPos of(Level world, BlockPos pos, @Nullable Direction side) {\n        return of(DimPos.of(world, pos), side);\n    }\n\n    public static PartPos of(DimPos pos, @Nullable Direction side) {\n        return new PartPos(pos, side);\n    }\n\n    private PartPos(DimPos pos, @Nullable Direction side) {\n        this.pos = pos;\n        this.side = side;\n    }\n\n    public DimPos getPos() {\n        return pos;\n    }\n\n    @Nullable\n    public Direction getSide() {\n        return side;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || !(o instanceof PartPos)) return false;\n\n        PartPos partPos = (PartPos) o;\n\n        if (!pos.equals(partPos.pos)) return false;\n        return side == partPos.side;\n\n    }\n\n    @Override\n    public int hashCode() {\n        return 31 * pos.hashCode() + (side != null ? side.hashCode() : 0);\n    }\n\n    @Override\n    public String toString() {\n        return \"PartPos{\" +\n                \"pos=\" + pos +\n                \", side=\" + side +\n                '}';\n    }\n\n    /**\n     * Get part data from the given position.\n     * @param pos The part position.\n     * @return A pair of part type and part state or null if not found.\n     */\n    @Nullable\n    public static Pair<IPartType, IPartState> getPartData(PartPos pos) {\n        IPartContainer partContainer = PartHelpers.getPartContainer(pos.getPos(), pos.getSide()).orElse(null);\n        if (partContainer != null) {\n            IPartType partType = partContainer.getPart(pos.getSide());\n            IPartState partState = partContainer.getPartState(pos.getSide());\n            if (partType != null && partState != null) {\n                return Pair.of(partType, partState);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public int compareTo(PartPos o) {\n        int pos = this.getPos().compareTo(o.getPos());\n        if (pos == 0) {\n            Direction thisSide = this.getSide();\n            Direction thatSide = o.getSide();\n            return thisSide == null ? (thatSide == null ? 0 : -1) : (thatSide == null ? 1 : thisSide.compareTo(thatSide));\n        }\n        return pos;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PartRenderPosition.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.cyclopscore.helper.MatrixHelpers;\nimport org.cyclops.integrateddynamics.block.shapes.CollisionContextBlockSupport;\nimport org.cyclops.integrateddynamics.client.model.CableModel;\n\nimport java.util.Arrays;\n\n/**\n * Render position definition of a part.\n * @author rubensworks\n */\npublic class PartRenderPosition {\n\n    public static final PartRenderPosition NONE = new PartRenderPosition(-1, -1, -1, -1);\n\n    private final float depthFactor;\n    private final float widthFactor;\n    private final float heightFactor;\n    private final float widthFactorSide;\n    private final float heightFactorSide;\n    private final EnumFacingMap<VoxelShape> sidedCableCollisionBoxes;\n    private final EnumFacingMap<VoxelShape> collisionBoxes;\n    private final EnumFacingMap<VoxelShape> collisionBoxesBlockSupport;\n\n    public PartRenderPosition(float selectionDepthFactor, float depthFactor, float widthFactor, float heightFactor) {\n        this(selectionDepthFactor, depthFactor, widthFactor, heightFactor, widthFactor, heightFactor);\n    }\n\n    public PartRenderPosition(float selectionDepthFactor, float depthFactor, float widthFactor, float heightFactor,\n                              float widthFactorSide, float heightFactorSide) {\n        this.depthFactor = depthFactor;\n        this.widthFactor = widthFactor;\n        this.heightFactor = heightFactor;\n        this.widthFactorSide = widthFactorSide;\n        this.heightFactorSide = heightFactorSide;\n        float[][] sidedCableCollisionBoxesRaw = new float[][]{\n                {CableModel.MIN, selectionDepthFactor, CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX}, // DOWN\n                {CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX, 1 - selectionDepthFactor, CableModel.MAX}, // UP\n                {CableModel.MIN, CableModel.MIN, selectionDepthFactor, CableModel.MAX, CableModel.MAX, CableModel.MIN}, // NORTH\n                {CableModel.MIN, CableModel.MAX, CableModel.MAX, CableModel.MAX, CableModel.MIN, 1 - selectionDepthFactor}, // SOUTH\n                {selectionDepthFactor, CableModel.MIN, CableModel.MIN, CableModel.MIN, CableModel.MAX, CableModel.MAX}, // WEST\n                {CableModel.MAX, CableModel.MIN, CableModel.MIN, 1 - selectionDepthFactor, CableModel.MAX, CableModel.MAX}, // EAST\n        };\n\n        sidedCableCollisionBoxes = EnumFacingMap.newMap();\n        for (Direction side : Direction.values()) {\n            float[] b = sidedCableCollisionBoxesRaw[side.ordinal()];\n            sidedCableCollisionBoxes.put(side, Shapes.create(new AABB(b[0], b[1], b[2], b[3], b[4], b[5])));\n        }\n\n        float min = (1 - widthFactor) / 2 + 0.0025F;\n        float max = (1 - widthFactor) / 2 + widthFactor - 0.0025F;\n        float[][] collisionBoxesRaw = new float[][]{\n                {min, max}, {0.005F, selectionDepthFactor}, {min, max}\n        };\n        float[][] collisionBoxesBlockSupportRaw = new float[][]{\n                {0, 1}, {0, selectionDepthFactor}, {0, 1}\n        };\n        collisionBoxes = EnumFacingMap.newMap();\n        collisionBoxesBlockSupport = EnumFacingMap.newMap();\n        for (Direction side : Direction.values()) {\n            // Copy bounds\n            float[][] bounds = new float[collisionBoxesRaw.length][collisionBoxesRaw[0].length];\n            for (int i = 0; i < bounds.length; i++)\n                bounds[i] = Arrays.copyOf(collisionBoxesRaw[i], collisionBoxesRaw[i].length);\n\n            // Transform bounds\n            MatrixHelpers.transform(bounds, side);\n            collisionBoxes.put(side, Shapes.create(new AABB(bounds[0][0], bounds[1][0], bounds[2][0], bounds[0][1], bounds[1][1], bounds[2][1])));\n\n            // Copy bounds block support\n            float[][] boundsBS = new float[collisionBoxesBlockSupportRaw.length][collisionBoxesBlockSupportRaw[0].length];\n            for (int i = 0; i < boundsBS.length; i++)\n                boundsBS[i] = Arrays.copyOf(collisionBoxesBlockSupportRaw[i], collisionBoxesBlockSupportRaw[i].length);\n\n            // Transform bounds block support\n            MatrixHelpers.transform(boundsBS, side);\n            collisionBoxesBlockSupport.put(side, Shapes.create(new AABB(boundsBS[0][0], boundsBS[1][0], boundsBS[2][0], boundsBS[0][1], boundsBS[1][1], boundsBS[2][1])));\n        }\n    }\n\n    public float getDepthFactor() {\n        return depthFactor;\n    }\n\n    public float getWidthFactor() {\n        return widthFactor;\n    }\n\n    public float getHeightFactor() {\n        return heightFactor;\n    }\n\n    public VoxelShape getSidedCableBoundingBox(Direction side) {\n        return sidedCableCollisionBoxes.get(side);\n    }\n\n    public VoxelShape getBoundingBox(Direction side, CollisionContext context) {\n        if (context instanceof CollisionContextBlockSupport) {\n            return collisionBoxesBlockSupport.get(side);\n        }\n        return collisionBoxes.get(side);\n    }\n\n    public float getWidthFactorSide() {\n        return widthFactorSide;\n    }\n\n    public float getHeightFactorSide() {\n        return heightFactorSide;\n    }\n\n    @Override\n    public String toString() {\n        return \"PartRenderPosition{\" +\n                \"depthFactor=\" + depthFactor +\n                \", widthFactor=\" + widthFactor +\n                \", heightFactor=\" + heightFactor +\n                \", widthFactorSide=\" + widthFactorSide +\n                \", heightFactorSide=\" + heightFactorSide +\n                '}';\n    }\n\n    public String toCompactString() {\n        return \"df=\" + depthFactor +\n                \",wf=\" + widthFactor +\n                \",hf=\" + heightFactor +\n                \",wfs=\" + widthFactorSide +\n                \",hfs=\" + heightFactorSide;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PartTarget.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\n\nimport javax.annotation.Nullable;\n\n/**\n * Object holder to refer to another block side and its origin.\n * @author rubensworks\n */\npublic class PartTarget {\n\n    private final PartPos center;\n    private final PartPos target;\n\n    /**\n     * Get the target from a center block that is targeted at another block.\n     * @param pos The central position that is referring to the target.\n     * @param side The side on the central position that points to the target.\n     * @return The target referral.\n     */\n    public static PartTarget fromCenter(DimPos pos, @Nullable Direction side) {\n        return PartTarget.of(\n                PartPos.of(pos, side),\n                PartPos.of(DimPos.of(pos.getLevelKey(), side == null ? pos.getBlockPos() : pos.getBlockPos().relative(side)), side == null ? null : side.getOpposite())\n        );\n    }\n\n    /**\n     * Get the target from a center block that is targeted at another block.\n     * @param pos The central position that is referring to the target.\n     * @return The target referral.\n     */\n    public static PartTarget fromCenter(PartPos pos) {\n        return fromCenter(pos.getPos(), pos.getSide());\n    }\n\n    /**\n     * Get the target from a center block that is targeted at another block.\n     * @param world The world.\n     * @param pos The central position that is referring to the target.\n     * @param side The side on the central position that points to the target.\n     * @return The target referral.\n     */\n    public static PartTarget fromCenter(Level world, BlockPos pos, Direction side) {\n        return PartTarget.fromCenter(DimPos.of(world, pos), side);\n    }\n\n    /**\n     * Create a new instance.\n     * @param center The center position.\n     * @param target The target position.\n     * @return The target.\n     */\n    public static PartTarget of(PartPos center, PartPos target) {\n        return new PartTarget(center, target);\n    }\n\n    public PartPos getCenter() {\n        return center;\n    }\n\n    public PartPos getTarget() {\n        return target;\n    }\n\n    public boolean isDefaultTarget() {\n        return getCenter().getPos().getBlockPos().relative(getCenter().getSide()).equals(getTarget().getPos().getBlockPos());\n    }\n\n    private PartTarget(PartPos center, PartPos target) {\n        this.center = center;\n        this.target = target;\n    }\n\n    /**\n     * Create a new instance with the given target side.\n     * @param targetSide The side of the target.\n     * @return A new {@link PartTarget} instance.\n     */\n    public PartTarget forTargetSide(Direction targetSide) {\n        return new PartTarget(center, PartPos.of(target.getPos(), targetSide));\n    }\n\n    /**\n     * Create a new instance with the given target offset.\n     * @param offset The offset of the target.\n     * @return A new {@link PartTarget} instance.\n     */\n    public PartTarget forOffset(Vec3i offset) {\n        return new PartTarget(center, PartPos.of(target.getPos().withPosition(target.getPos().getBlockPos().offset(offset)), target.getSide()));\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof PartTarget)) return false;\n\n        PartTarget that = (PartTarget) o;\n\n        if (!center.equals(that.center)) return false;\n        return target.equals(that.target);\n\n    }\n\n    @Override\n    public int hashCode() {\n        int result = center.hashCode();\n        result = 31 * result + target.hashCode();\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"PartTarget{\" +\n                \"center=\" + center +\n                \", target=\" + target +\n                '}';\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PartTypeAdapter.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.util.RandomSource;\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.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.slf4j.Logger;\n\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\n/**\n * Default implementation of {@link IPartType}.\n * @author rubensworks\n */\npublic abstract class PartTypeAdapter<P extends IPartType<P, S>, S extends IPartState<P>> implements IPartType<P, S> {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n\n    private String translationKey = null;\n\n    @Override\n    public final String getTranslationKey() {\n        return translationKey != null ? translationKey : (translationKey = createTranslationKey());\n    }\n\n    protected abstract String createTranslationKey();\n\n    @Override\n    public boolean isSolid(S state) {\n        return false;\n    }\n\n    @Override\n    public void serializeState(ValueOutput valueOutput, S partState) {\n        partState.serialize(valueOutput);\n    }\n\n    @Override\n    public S deserializeState(ValueInput valueInput) {\n        S partState = constructDefaultState();\n        partState.deserialize(valueInput);\n        partState.gatherCapabilities((P) this);\n        return partState;\n    }\n\n    @Override\n    public void setUpdateInterval(S state, int updateInterval) {\n        state.setUpdateInterval(updateInterval);\n    }\n\n    @Override\n    public int getUpdateInterval(S state) {\n        return Math.max(getMinimumUpdateInterval(state), state.getUpdateInterval());\n    }\n\n    @Override\n    public int getMinimumUpdateInterval(S state) {\n        return Math.max(GeneralConfig.partsMinimumUpdateInterval, GeneralConfig.partMinimumUpdateIntervalsMap.getOrDefault(this, 1));\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, IPartNetwork partNetwork, PartTarget target, S state, int priority, int channel) {\n        //noinspection deprecation\n        state.setPriority(priority);\n        state.setChannel(channel);\n    }\n\n    @Override\n    public int getPriority(S state) {\n        return state.getPriority();\n    }\n\n    @Override\n    public int getChannel(S state) {\n        return state.getChannel();\n    }\n\n    @Override\n    public Vec3i getTargetOffset(S state) {\n        return state.getTargetOffset();\n    }\n\n    @Override\n    public boolean setTargetOffset(S state, PartPos center, Vec3i offset) {\n        int max = state.getMaxOffset();\n        if (offset.getX() >= -max && offset.getY() >= -max && offset.getZ() >= -max\n                && offset.getX() <= max && offset.getY() <= max && offset.getZ() <= max) {\n            state.setTargetOffset(offset);\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public void setTargetSideOverride(S state, @Nullable Direction side) {\n        state.setTargetSideOverride(side);\n    }\n\n    @Nullable\n    @Override\n    public Direction getTargetSideOverride(S state) {\n        return state.getTargetSideOverride();\n    }\n\n    @Override\n    public PartTarget getTarget(PartPos pos, S state) {\n        PartTarget target = PartTarget.fromCenter(pos);\n        Direction sideOverride = getTargetSideOverride(state);\n        if (sideOverride != null) {\n            target = target.forTargetSide(sideOverride);\n        }\n        Vec3i offset = getTargetOffset(state);\n        if (offset.compareTo(Vec3i.ZERO) != 0) {\n            target = target.forOffset(offset);\n        }\n        return target;\n    }\n\n    protected boolean hasOffsetVariables(S state) {\n        NonNullList<ItemStack> inventory = state.getInventoryNamed(\"offsetVariablesInventory\");\n        return inventory != null && inventory.stream().anyMatch(item -> !item.isEmpty());\n    }\n\n    @Override\n    public void onOffsetVariablesChanged(PartTarget target, S state) {\n        state.markOffsetVariablesChanged();\n    }\n\n    @Override\n    public boolean isUpdate(S state) {\n        return hasOffsetVariables(state);\n    }\n\n    @Override\n    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        state.updateOffsetVariables((P) this, network, partNetwork, target);\n    }\n\n    @Override\n    public void beforeNetworkKill(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n\n    }\n\n    @Override\n    public void afterNetworkAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n\n    }\n\n    @Override\n    public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        // This resets any errored offset variables and forces them to reload.\n        state.markOffsetVariablesChanged();\n    }\n\n    @Override\n    public ItemStack getItemStack(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, S state, boolean saveState) {\n        ItemStack itemStack = new ItemStack(getItem());\n        if (saveState) {\n            try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(problemPath, LOGGER)) {\n                TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n                serializeState(valueOutput, state);\n                itemStack.set(RegistryEntries.DATACOMPONENT_PART_STATE, valueOutput.buildResult());\n            }\n        }\n        return itemStack;\n    }\n\n    @Override\n    public ItemStack getCloneItemStack(Level world, BlockPos pos, S state) {\n        return getItemStack(ValueDeseralizationContext.of(world), new PartPathElement(pos), state, false);\n    }\n\n    @Override\n    public S getState(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, ItemStack itemStack) {\n        S partState = null;\n        if(!itemStack.isEmpty() && itemStack.has(RegistryEntries.DATACOMPONENT_PART_STATE)) {\n            try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(problemPath, LOGGER)) {\n                ValueInput input = TagValueInput.create(\n                        scopedCollector,\n                        valueDeseralizationContext.holderLookupProvider(),\n                        itemStack.get(RegistryEntries.DATACOMPONENT_PART_STATE)\n                );\n                partState = deserializeState(input);\n            }\n        }\n        if(partState == null) {\n            partState = defaultBlockState();\n        }\n        return partState;\n    }\n\n    /**\n     * @return Constructor call for a new default state for this part type.\n     */\n    protected abstract S constructDefaultState();\n\n    @Override\n    public S defaultBlockState() {\n        S defaultState = constructDefaultState();\n        defaultState.generateId();\n        defaultState.gatherCapabilities((P) this);\n        return defaultState;\n    }\n\n    @Override\n    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n        if(dropMainElement) {\n            itemStacks.add(getItemStack(ValueDeseralizationContext.of(target.getCenter().getPos().getLevel(true)), new PartPathElement(target.getCenter().getPos().getBlockPos()), state, saveState));\n        }\n\n        // Drop contents of named inventories\n        for (Map.Entry<String, NonNullList<ItemStack>> entry : state.getInventoriesNamed().entrySet()) {\n            for (ItemStack itemStack : entry.getValue()) {\n                if (!itemStack.isEmpty()) {\n                    itemStacks.add(itemStack);\n                }\n            }\n        }\n        state.clearInventoriesNamed();\n    }\n\n    @Override\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        state.initializeOffsets(target);\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n\n    }\n\n    @Override\n    public InteractionResult onPartActivated(S partState, BlockPos pos, Level world, Player player, InteractionHand hand, ItemStack heldItem, BlockHitResult hit) {\n        return InteractionResult.PASS;\n    }\n\n    @Override\n    public void updateTick(Level world, BlockPos pos, S partState, RandomSource random) {\n\n    }\n\n    @Override\n    public void onPreRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n\n    }\n\n    @Override\n    public void onBlockNeighborChange(INetwork network, IPartNetwork partNetwork, PartTarget target, S state,\n                                      BlockGetter world, @Nullable Direction side) {\n\n    }\n\n    @Override\n    public int getConsumptionRate(S state) {\n        return 0;\n    }\n\n    @Override\n    public void postUpdate(INetwork network, IPartNetwork partNetwork, PartTarget target, S state, boolean updated) {\n        setEnabled(state, updated);\n    }\n\n    @Override\n    public boolean isEnabled(S state) {\n        return state.isEnabled();\n    }\n\n    @Override\n    public void setEnabled(S state, boolean enabled) {\n        state.setEnabled(enabled);\n    }\n\n    @Override\n    public void loadTooltip(S state, List<Component> lines) {\n\n    }\n\n    @Override\n    public void loadTooltip(ItemStack itemStack, Consumer<Component> tooltipAdder) {\n\n    }\n\n    @Override\n    public boolean shouldTriggerBlockRenderUpdate(@Nullable S oldPartState, @Nullable S newPartState) {\n        return oldPartState == null || newPartState == null || oldPartState.isForceBlockRenderUpdateAndReset();\n    }\n\n    @Override\n    public boolean hasEventSubscriptions() {\n        return false;\n    }\n\n    @Override\n    public Set<Class<? extends INetworkEvent>> getSubscribedEvents() {\n        return Collections.emptySet();\n    }\n\n    @Override\n    public void onEvent(INetworkEvent event, IPartNetworkElement<P, S> networkElement) {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/PrioritizedPartPos.java",
    "content": "package org.cyclops.integrateddynamics.api.part;\n\nimport net.minecraft.core.Direction;\n\n/**\n * A wrapper around {@link PartPos} that also holds a priority.\n *\n * This is useful for automatically providing sorted collections.\n *\n * @author rubensworks\n */\npublic class PrioritizedPartPos implements Comparable<PrioritizedPartPos> {\n    private final PartPos partPos;\n    private final int priority;\n\n    private PrioritizedPartPos(PartPos partPos, int priority) {\n        this.partPos = partPos;\n        this.priority = priority;\n    }\n\n    @Override\n    public int compareTo(PrioritizedPartPos o) {\n        int compPriority = -Integer.compare(this.getPriority(), o.getPriority());\n        if (compPriority == 0) {\n            int compPos = this.getPartPos().getPos().compareTo(o.getPartPos().getPos());\n            if (compPos == 0) {\n                Direction thisSide = this.getPartPos().getSide();\n                Direction otherSide = o.getPartPos().getSide();\n                return thisSide == null ? (otherSide == null ? 0 : -1) : (otherSide == null ? 1 : thisSide.compareTo(otherSide));\n            }\n            return compPos;\n        }\n        return compPriority;\n    }\n\n    public static PrioritizedPartPos of(PartPos pos, int priority) {\n        return new PrioritizedPartPos(pos, priority);\n    }\n\n    public PartPos getPartPos() {\n        return partPos;\n    }\n\n    public int getPriority() {\n        return priority;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        return obj instanceof PrioritizedPartPos && compareTo((PrioritizedPartPos) obj) == 0;\n    }\n\n    @Override\n    public int hashCode() {\n        return getPartPos().hashCode() + getPriority() << 1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/AspectUpdateType.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.ScheduledTickAccess;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.Orientation;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * Different types of aspect update triggers.\n * I.e., when {@link IAspect#update(org.cyclops.integrateddynamics.api.network.INetwork, IPartNetwork, IPartType, PartTarget, IPartState)} should be called.\n * @author rubensworks\n */\npublic enum AspectUpdateType {\n    /**\n     * Update per network tick.\n     */\n    NETWORK_TICK,\n    /**\n     * Update its value on block neigbour changes,\n     * i.e., if {@link Block#neighborChanged(BlockState, Level, BlockPos, Block, Orientation, boolean)} ,\n     * {@link Block#onNeighborChange(BlockState, LevelReader, BlockPos, BlockPos)}\n     * or {@link Block#updateShape(BlockState, LevelReader, ScheduledTickAccess, BlockPos, Direction, BlockPos, BlockState, RandomSource)}  is called.\n     */\n    BLOCK_UPDATE,\n    /**\n     * If the update method should never be called.\n     */\n    NEVER\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IAspect.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.MenuProvider;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.function.Consumer;\n\n/**\n * An element that can be used inside parts to access a specific aspect of something to read/write.\n * @param <V> The value type.\n * @param <T> The value type type.\n * @author rubensworks\n */\npublic interface IAspect<V extends IValue, T extends IValueType<V>> {\n\n    /**\n     * @return The unique name for this aspecy, only used for internal storage.\n     */\n    public Identifier getUniqueName();\n\n    /**\n     * @return The unique unlocalized name for this aspect.\n     */\n    public String getTranslationKey();\n\n    /**\n     * Add tooltip lines for this aspect when hovered in a gui.\n     *\n     * @param tooltipAdder       The list to add lines to.\n     * @param appendOptionalInfo If shift-to-show info should be added.\n     */\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo);\n\n    /**\n     * @return The type of value this aspect can handle.\n     */\n    public T getValueType();\n\n    /**\n     * Called inside part types for updating a part on a block.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     * @param network The network to update in.\n     * @param partNetwork The part network to update in.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void update(INetwork network, IPartNetwork partNetwork, P partType, PartTarget target, S state);\n\n    /**\n     * @return If this aspect supports additional properties.\n     * @param <P> The part type type\n     * @param <S> The part state type\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> boolean hasProperties();\n\n    /**\n     * Get the current properties of this aspect in the given part.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @return The current properties.\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> IAspectProperties getProperties(P partType, PartTarget target, S state);\n\n    /**\n     * Set the new properties of this aspect in the given part.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @param properties The new properties.\n     */\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void setProperties(P partType, PartTarget target, S state, IAspectProperties properties);\n\n    /**\n     * @return The default properties for this aspect.\n     */\n    @Nullable\n    public IAspectProperties getDefaultProperties();\n\n    /**\n     * These are the properties that are supported for this aspect.\n     * It is possible that some deprecated properties are available inside the retrieved properties, so use\n     * this to iterate over the values.\n     * @return The types that are available for this aspect.\n     */\n    public Collection<IAspectPropertyTypeInstance> getPropertyTypes();\n\n    /**\n     * This will only be called if this aspect has properties.\n     * @return The container type for the screen to configure the properties.\n     * @param pos The part position.\n     */\n    public MenuProvider getPropertiesContainerProvider(PartPos pos);\n\n    /**\n     * Use this comparator for any comparisons with aspects.\n     */\n    public static class AspectComparator implements Comparator<IAspect> {\n\n        private static AspectComparator INSTANCE = null;\n\n        private AspectComparator() {\n\n        }\n\n        public static AspectComparator getInstance() {\n            if(INSTANCE == null) INSTANCE = new AspectComparator();\n            return INSTANCE;\n        }\n\n        @Override\n        public int compare(IAspect o1, IAspect o2) {\n            return o1.getUniqueName().compareTo(o2.getUniqueName());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IAspectRead.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\nimport java.util.function.Supplier;\n\n/**\n * An element that can be used inside parts to access a specific aspect of something to read.\n * @author rubensworks\n */\npublic interface IAspectRead<V extends IValue, T extends IValueType<V>> extends IAspect<V, T> {\n\n    /**\n     * Creates a new variable for this aspect.\n     * @param targetSupplier The target supplier for this aspect.\n     * @return The variable pointing to the given target.\n     */\n    public IAspectVariable<V> createNewVariable(Supplier<PartTarget> targetSupplier);\n\n    /**\n     * @return The update type on which this aspect should invalidate.\n     */\n    public AspectUpdateType getUpdateType();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IAspectRegistry.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.init.IRegistry;\nimport org.cyclops.integrateddynamics.api.item.IAspectVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Registry for {@link IAspect}.\n * @author rubensworks\n */\npublic interface IAspectRegistry extends IRegistry, IVariableFacadeHandler<IAspectVariableFacade> {\n\n    public IAspectRegistryClient getClient();\n\n    /**\n     * Register a new aspect for a given part type.\n     * @param partType The part type.\n     * @param aspect The aspect.\n     * @return The registered element.\n     */\n    public IAspect register(IPartType partType, IAspect aspect);\n\n    /**\n     * Register a set of aspects for a given part type.\n     * @param partType The part type.\n     * @param aspects The aspects.\n     */\n    public void register(IPartType partType, Collection<IAspect> aspects);\n\n    /**\n     * Get the registered aspects for a given part type.\n     * @param partType The part type.\n     * @return The aspects.\n     */\n    public Set<IAspect> getAspects(IPartType partType);\n\n    /**\n     * Get the registered read aspects for a given part type.\n     * @param partType The part type.\n     * @return The read aspects.\n     */\n    public List<IAspectRead<?, ?>> getReadAspects(IPartType partType);\n\n    /**\n     * Get the registered write aspects for a given part type.\n     * @param partType The part type.\n     * @return The write aspects.\n     */\n    public List<IAspectWrite<?, ?>> getWriteAspects(IPartType partType);\n\n    /**\n     * Get all registered aspects.\n     * @return The aspects.\n     */\n    public Set<IAspect> getAspects();\n\n    /**\n     * Get all registered read aspects.\n     * @return The read aspects.\n     */\n    public Set<IAspectRead> getReadAspects();\n\n    /**\n     * Get all registered write aspects.\n     * @return The write aspects.\n     */\n    public Set<IAspectWrite> getWriteAspects();\n\n    /**\n     * Get an aspect by name.\n     * @param name The name of the aspect.\n     * @return The matching aspect.\n     */\n    public IAspect getAspect(Identifier name);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IAspectRegistryClient.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.resources.Identifier;\n\nimport java.util.Collection;\n\n/**\n * @author rubensworks\n */\npublic interface IAspectRegistryClient {\n\n    /**\n     * Register a facadeModel resource location for the given aspect.\n     * @param aspect The aspect.\n     * @param modelLocation The facadeModel resource location.\n     */\n    public void registerAspectModel(IAspect aspect, Identifier modelLocation);\n\n    /**\n     * Get the facadeModel resource location of the given aspect.\n     *\n     * @param aspect The aspect.\n     * @return The unbaked facadeModel.\n     */\n    public ItemModel.Unbaked getAspectModel(IAspect aspect);\n\n    /**\n     * Get all registered facadeModel resource locations for the aspects.\n     * @return All facadeModel resource locations.\n     */\n    public Collection<ItemModel.Unbaked> getAspectModels();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IAspectVariable.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * A separate instance of this exists for each part.\n * @author rubensworks\n */\npublic interface IAspectVariable<V extends IValue> extends IVariable<V> {\n\n    /**\n     * @return The target of this aspect variable.\n     */\n    public PartTarget getTarget();\n\n    /**\n     * @return The referenced aspect.\n     */\n    public IAspectRead<V, ?> getAspect();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IAspectWrite.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\n\n/**\n * An element that can be used inside parts to access a specific aspect of something to write.\n * @author rubensworks\n */\npublic interface IAspectWrite<V extends IValue, T extends IValueType<V>> extends IAspect<V, T> {\n\n    /**\n     * Write the given variable value for the given part.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @param variable The variable to write.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     * @throws EvaluationException If something went wrong while evaluating the variable to write.\n     */\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void write(P partType, PartTarget target,\n                                                                                       S state, IVariable<V> variable)\n            throws EvaluationException;\n\n    /**\n     * When this aspect has become active.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     */\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onActivate(P partType,\n                                                                                              PartTarget target, S state);\n\n    /**\n     * When this aspect has become inactive.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     */\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onDeactivate(P partType,\n                                                                                              PartTarget target, S state);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/IPositionedVariable.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\n\n/**\n * A variable that exists at a certain position.\n * @author rubensworks\n */\npublic interface IPositionedVariable<V extends IValue> extends IVariable<V> {\n\n    /**\n     * @return The target of this aspect variable.\n     */\n    public DimPos getTarget();\n\n    /**\n     * @return If this variable requires updating.\n     */\n    public boolean requiresUpdate();\n\n    /**\n     * Called when this variable should update.\n     * This is only called when required, so there is no guarantee that this is called in a regular pattern.\n     */\n    public void update();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/property/IAspectProperties.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect.property;\n\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\n\nimport java.util.Collection;\n\n/**\n * A property that can be used inside aspects.\n * @author rubensworks\n */\npublic interface IAspectProperties {\n    /**\n     * Use this with caution!\n     * Better to use {@link IAspect#getPropertyTypes()} instead because this object might hold deprecated elements.\n     * @deprecated Use {@link IAspect#getPropertyTypes()}.\n     * @return The types.\n     */\n    @Deprecated\n    public Collection<IAspectPropertyTypeInstance> getTypes();\n\n    /**\n     * Get the value of the given type.\n     * @param type The type to get the value from.\n     * @param <T> The value type type.\n     * @param <V> The value type.\n     * @return The value.\n     */\n    public <T extends IValueType<V>, V extends IValue> V getValue(IAspectPropertyTypeInstance<T, V> type);\n\n    /**\n     * Set the value for the given type.\n     * @param type The type to get the value from.\n     * @param <T> The value type type.\n     * @param <V> The value type.\n     * @param value The value.\n     */\n    public <T extends IValueType<V>, V extends IValue> void setValue(IAspectPropertyTypeInstance<T, V> type, V value);\n\n    /**\n     * Remove the value of the given type.\n     * @param type The type to get the value from.\n     * @param <T> The value type type.\n     * @param <V> The value type.\n     */\n    public <T extends IValueType<V>, V extends IValue> void removeValue(IAspectPropertyTypeInstance<T, V> type);\n\n    /**\n     * @return A deep copy of the properties.\n     */\n    @SuppressWarnings({\"CloneDoesntCallSuperClone\", \"deprecation\"})\n    public IAspectProperties clone();\n\n\n    /**\n     * Serialize the data.\n     * @param valueOutput The value output.\n     */\n    public void serialize(ValueOutput valueOutput);\n\n    /**\n     * Read the data and place it in this object.\n     * The given tag will never be null, so make sure that all fields have a correct default value in case\n     * the received tag would be null anyways.\n     *\n     * @param valueInput The value input.\n     */\n    public void deserialize(ValueInput valueInput);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/aspect/property/IAspectPropertyTypeInstance.java",
    "content": "package org.cyclops.integrateddynamics.api.part.aspect.property;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport java.util.function.Predicate;\n\n/**\n * An instance of a property type with a onLabelPacket.\n * @author rubensworks\n */\npublic interface IAspectPropertyTypeInstance<T extends IValueType<V>, V extends IValue> {\n\n    /**\n     * @return The value type of this property.\n     */\n    public T getType();\n\n    /**\n     * @return The unique name of this property, also used for localization.\n     */\n    String getTranslationKey();\n\n    /**\n     * @return The value validator.\n     */\n    Predicate<V> getValidator();\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/read/IPartStateReader.java",
    "content": "package org.cyclops.integrateddynamics.api.part.read;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\n\n/**\n * A value holder for an {@link IPartTypeReader}.\n * This is what will be serialized from and to NBT.\n * This object is mutable and should not be recreated.\n * @author rubensworks\n */\npublic interface IPartStateReader<P extends IPartTypeReader> extends IPartState<P> {\n\n    /**\n     * Get the singleton variable for an aspect.\n     * This only retrieves the previously stored state.\n     * Better to call {@link IPartTypeReader#getVariable(PartTarget, IPartStateReader, IAspectRead)}.\n     * @param aspect The aspect from the part of this state.\n     * @param <V> The value type\n     * @param <T> The value type type\n     * @return The variable that exists only once for an aspect in this part state.\n     */\n    public <V extends IValue, T extends IValueType<V>> IAspectVariable<V> getVariable(IAspectRead<V, T> aspect);\n\n    /**\n     * Get the singleton variable for an aspect.\n     * @param aspect The aspect from the part of this state.\n     * @param variable The variable that exists only once for an aspect in this part state.\n     */\n    public void setVariable(IAspect aspect, IAspectVariable variable);\n\n    /**\n     * Reset all cached aspect variables.\n     */\n    public void resetVariables();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/read/IPartTypeReader.java",
    "content": "package org.cyclops.integrateddynamics.api.part.read;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\n\nimport java.util.List;\n\n/**\n * A part type for readers.\n * @author rubensworks\n */\npublic interface IPartTypeReader<P extends IPartTypeReader<P, S>, S extends IPartStateReader<P>> extends IPartType<P, S> {\n\n    /**\n     * @return All possible read aspects that can be used in this part type.\n     */\n    public List<IAspectRead<?, ?>> getReadAspects();\n\n    /**\n     * Get the singleton variable for an aspect.\n     * @param target The target block.\n     * @param partState The state of this part.\n     * @param aspect The aspect from the part of this state.\n     * @param <V> The value type.\n     * @param <T> The value type type.\n     * @return The variable that exists only once for an aspect in the given part state.\n     */\n    public <V extends IValue, T extends IValueType<V>> IAspectVariable<V> getVariable(PartTarget target, S partState,\n                                                                                      IAspectRead<V, T> aspect);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/write/IPartStateWriter.java",
    "content": "package org.cyclops.integrateddynamics.api.part.write;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * A value holder for an {@link IPartTypeWriter}.\n * This is what will be serialized from and to NBT.\n * This object is mutable and should not be recreated.\n * @author rubensworks\n */\npublic interface IPartStateWriter<P extends IPartTypeWriter> extends IPartState<P> {\n\n    /**\n     * @return The inventory for this state.\n     */\n    public SimpleInventory getInventory();\n\n    /**\n     * @return If there is an active variable present for this state.\n     */\n    public boolean hasVariable();\n\n    /**\n     * Get the current target variable.\n     * @param <V> The value type.\n     * @param network The network this part belongs to.\n     * @param partNetwork The part network this part belongs to.\n     * @param valueDeseralizationContext The value deserialization context.\n     * @return The active variable to read from.\n     */\n    public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork, ValueDeseralizationContext valueDeseralizationContext);\n\n    /**\n     * Indicate that this state should eventually recheck its aspect info because something might have changed what can\n     * cause the active variable to be referring to something else.\n     *\n     * @param partType              The part type.\n     * @param target                The target.\n     * @param newAspect             The new active aspect, can be null.\n     * @param isNetworkInitializing If this method is being invoked during network (de)initializing.\n     */\n    public void triggerAspectInfoUpdate(P partType, PartTarget target, IAspectWrite newAspect, boolean isNetworkInitializing);\n\n    /**\n     * Called when this part should refresh its state.\n     * When for example some variables in the network are changed.\n     * @param partType The part type.\n     * @param target The target.\n     */\n    public void onVariableContentsUpdated(P partType, PartTarget target);\n\n    /**\n     * @return The currently active aspect for this part, can be null.\n     */\n    @Nullable\n    public IAspectWrite getActiveAspect();\n\n    /**\n     * Get the current error for the given aspect.\n     * @param aspect The aspect to get the error from.\n     * @return The current error, can be empty.\n     */\n    public List<Component> getErrors(IAspectWrite aspect);\n\n    /**\n     * Set the current error for the given aspect.\n     * @param aspect The aspect to set the error for.\n     * @param error The error to set, or null to clear.\n     */\n    public void addError(IAspectWrite aspect, MutableComponent error);\n\n    /**\n     * @return If this part has been deactivated.\n     */\n    public boolean isDeactivated();\n\n    /**\n     * Used to avoid calling deactivation logic more than once when updating aspects.\n     * @param deactivated If this part should be deactivated.\n     */\n    public void setDeactivated(boolean deactivated);\n\n    /**\n     * If this state is at its first tick, will be reset after calling this.\n     * @return If this is the first time this state is ticking.\n     */\n    public boolean checkAndResetFirstTick();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/part/write/IPartTypeWriter.java",
    "content": "package org.cyclops.integrateddynamics.api.part.write;\n\nimport net.minecraft.world.entity.player.Player;\nimport org.cyclops.integrateddynamics.api.part.IPartTypeActiveVariable;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * A part type for writers.\n * @author rubensworks\n */\npublic interface IPartTypeWriter<P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> extends IPartTypeActiveVariable<P, S> {\n\n    /**\n     * @return All possible write aspects that can be used in this part type.\n     */\n    public List<IAspectWrite<?, ?>> getWriteAspects();\n\n    /**\n     * Get the aspect that is currently active in this part, can be null.\n     * @param target The target block.\n     * @param partState The state of this part.\n     * @return The active aspect.\n     */\n    public IAspectWrite getActiveAspect(PartTarget target, S partState);\n\n    /**\n     * Update the active aspect and active variable for this part.\n     *\n     * @param target                The target block.\n     * @param partState             The state of this part.\n     * @param player                The player activating the aspect, can be null.\n     * @param isNetworkInitializing\n     */\n    public void updateActivation(PartTarget target, S partState, @Nullable Player player, boolean isNetworkInitializing);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/path/IPathElement.java",
    "content": "package org.cyclops.integrateddynamics.api.path;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\n\nimport java.util.Set;\n\n/**\n * An element that can be used to construct paths using the\n * {@link org.cyclops.integrateddynamics.core.path.PathFinder}.\n * Multiple instances for the same 'element' can be created, so the comparator implementation must\n * make sure that these instances are considered equal.\n * These instances are used as a simple way of referring to these elements.\n * @author rubensworks\n */\npublic interface IPathElement extends Comparable<IPathElement> {\n\n    /**\n     * @return The position of this element.\n     */\n    public DimPos getPosition();\n\n    /**\n     * @return The set of all path elements that can be reached from here.\n     */\n    public Set<ISidedPathElement> getReachableElements();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/path/ISidedPathElement.java",
    "content": "package org.cyclops.integrateddynamics.api.path;\n\nimport net.minecraft.core.Direction;\n\nimport javax.annotation.Nullable;\n\n/**\n * A path element wrapped together with the side it was found on.\n * Multiple instances for the same 'element' can be created, so the comparator implementation must\n * make sure that these instances are considered equal.\n * @author rubensworks\n */\npublic interface ISidedPathElement extends Comparable<ISidedPathElement> {\n\n    /**\n     * @return The path element.\n     */\n    public IPathElement getPathElement();\n\n    /**\n     * @return The side the path element was found on.\n     */\n    @Nullable\n    public Direction getSide();\n\n    /**\n     * @return Parameters for (de)serializing the path element.\n     */\n    public SidedPathElementParams getParams();\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/api/path/SidedPathElementParams.java",
    "content": "package org.cyclops.integrateddynamics.api.path;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\n\nimport java.util.Optional;\n\n/**\n * @author rubensworks\n */\npublic record SidedPathElementParams(String dimension, BlockPos pos, Optional<Direction> side) {\n    public static final Codec<SidedPathElementParams> CODEC = RecordCodecBuilder.create(\n            builder -> builder.group(\n                            Codec.STRING.fieldOf(\"dimension\").forGetter(e -> e.dimension),\n                            BlockPos.CODEC.fieldOf(\"pos\").forGetter(e -> e.pos),\n                            Direction.CODEC.optionalFieldOf(\"side\").forGetter(e -> e.side)\n                    )\n                    .apply(builder, SidedPathElementParams::new)\n    );\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCable.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.client.renderer.chunk.RenderSectionRegion;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\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.*;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.RenderShape;\nimport net.minecraft.world.level.block.SimpleWaterloggedBlock;\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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.BooleanProperty;\nimport net.minecraft.world.level.material.Fluid;\nimport net.minecraft.world.level.material.FluidState;\nimport net.minecraft.world.level.material.Fluids;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.shapes.*;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport net.neoforged.neoforge.model.data.ModelProperty;\nimport org.cyclops.cyclopscore.block.BlockWithEntity;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.IDynamicLight;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.block.shapes.*;\nimport org.cyclops.integrateddynamics.client.model.CableModel;\nimport org.cyclops.integrateddynamics.client.model.IRenderState;\nimport org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * A block that is built up from different parts.\n * This block refers to a ticking part entity.\n * @author rubensworks\n */\npublic class BlockCable extends BlockWithEntity implements SimpleWaterloggedBlock {\n\n    public static final MapCodec<BlockCable> CODEC = simpleCodec(BlockCable::new);\n\n    public static final float BLOCK_HARDNESS = 3.0F;\n\n    public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;\n\n    // Model Properties\n    public static final ModelProperty<Boolean> REALCABLE = new ModelProperty<>();\n    public static final ModelProperty<Boolean>[] CONNECTED = new ModelProperty[6];\n    public static final ModelProperty<PartRenderPosition>[] PART_RENDERPOSITIONS = new ModelProperty[6];\n    public static final ModelProperty<Optional<BlockState>> FACADE = new ModelProperty<>();\n    static {\n        for(Direction side : Direction.values()) {\n            CONNECTED[side.ordinal()] = new ModelProperty<>();\n            PART_RENDERPOSITIONS[side.ordinal()] = new ModelProperty<>();\n        }\n    }\n    public static final ModelProperty<IPartContainer> PARTCONTAINER = new ModelProperty<>();\n    public static final ModelProperty<IRenderState> RENDERSTATE = new ModelProperty<>();\n\n    // Collision boxes\n    public final static AABB CABLE_CENTER_BOUNDINGBOX = new AABB(\n            CableModel.MIN, CableModel.MIN, CableModel.MIN, CableModel.MAX, CableModel.MAX, CableModel.MAX);\n    private final static EnumFacingMap<AABB> CABLE_SIDE_BOUNDINGBOXES = EnumFacingMap.forAllValues(\n            new AABB(CableModel.MIN, 0, CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX), // DOWN\n            new AABB(CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX, 1, CableModel.MAX), // UP\n            new AABB(CableModel.MIN, CableModel.MIN, 0, CableModel.MAX, CableModel.MAX, CableModel.MIN), // NORTH\n            new AABB(CableModel.MIN, CableModel.MAX, CableModel.MAX, CableModel.MAX, CableModel.MIN, 1), // SOUTH\n            new AABB(0, CableModel.MIN, CableModel.MIN, CableModel.MIN, CableModel.MAX, CableModel.MAX), // WEST\n            new AABB(CableModel.MAX, CableModel.MIN, CableModel.MIN, 1, CableModel.MAX, CableModel.MAX) // EAST\n    );\n\n    private final VoxelShapeComponentsFactory voxelShapeComponentsFactory = new VoxelShapeComponentsFactory(\n            new VoxelShapeComponentsFactoryHandlerCableCenter(),\n            new VoxelShapeComponentsFactoryHandlerCableConnections(),\n            new VoxelShapeComponentsFactoryHandlerParts(),\n            new VoxelShapeComponentsFactoryHandlerFacade()\n    );\n\n    private boolean disableCollisionBox = false;\n\n    public void setDisableCollisionBox(boolean disableCollisionBox) {\n        this.disableCollisionBox = disableCollisionBox;\n    }\n\n    /**\n     * Flag to skip expensive network initialization during bulk cable placement.\n     * When true, onCableAdded calls are skipped in onPlace.\n     * Network initialization should be done manually after all cables are placed.\n     */\n    public static boolean SKIP_NETWORK_INIT = false;\n\n    public BlockCable(Properties properties) {\n        super(properties, BlockEntityMultipartTicking::new);\n        this.registerDefaultState(this.stateDefinition.any().setValue(WATERLOGGED, false));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    public boolean useShapeForLightOcclusion(BlockState p_60576_) {\n        return true;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_MULTIPART_TICKING.get(), new BlockEntityMultipartTicking.Ticker<>());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        super.createBlockStateDefinition(builder);\n        builder.add(WATERLOGGED);\n    }\n\n    @Override\n    protected BlockState updateShape(BlockState state, LevelReader level, ScheduledTickAccess scheduledTickAccess, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {\n        if (level instanceof ServerLevel serverLevel) {\n            if (state.getValue(WATERLOGGED)) {\n                serverLevel.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));\n            }\n            NetworkHelpers.onElementProviderBlockNeighborChange(serverLevel, pos, direction);\n        }\n        return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random);\n    }\n\n    @Override\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        FluidState ifluidstate = context.getLevel().getFluidState(context.getClickedPos());\n        return this.defaultBlockState().setValue(WATERLOGGED, ifluidstate.getType() == Fluids.WATER);\n    }\n\n    @Override\n    public FluidState getFluidState(BlockState state) {\n        return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);\n    }\n\n    @Override\n    public boolean canPlaceLiquid(@org.jetbrains.annotations.Nullable LivingEntity entity, BlockGetter worldIn, BlockPos pos, BlockState blockState, Fluid fluidIn) {\n        return !blockState.getValue(BlockStateProperties.WATERLOGGED) && fluidIn == Fluids.WATER\n                && !(worldIn instanceof ILevelExtension levelExtension && CableHelpers.hasFacade(levelExtension, pos, blockState));\n    }\n\n    @Override\n    public void onBlockExploded(BlockState state, ServerLevel world, BlockPos blockPos, Explosion explosion) {\n        CableHelpers.setRemovingCable(true);\n        CableHelpers.onCableRemoving(world, blockPos, true, false, state, world.getBlockEntity(blockPos));\n        super.onBlockExploded(state, world, blockPos, explosion);\n        CableHelpers.onCableRemoved(world, blockPos);\n        CableHelpers.setRemovingCable(false);\n    }\n\n    @Override\n    public boolean onDestroyedByPlayer(BlockState state, Level world, BlockPos pos, Player player, ItemStack toolStack, boolean willHarvest, FluidState fluid) {\n        BlockRayTraceResultComponent rayTraceResult = getSelectedShape(state, world, pos, CollisionContext.of(player))\n                .rayTrace(pos, player);\n        if (rayTraceResult != null && rayTraceResult.getComponent().destroy(world, pos, player, false)) {\n            return false;\n        }\n        return rayTraceResult != null && super.onDestroyedByPlayer(state, world, pos, player, toolStack, willHarvest, fluid);\n    }\n\n    @Override\n    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {\n        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);\n\n        if (!CableHelpers.isRemovingCable() && !SKIP_NETWORK_INIT) {\n            CableHelpers.onCableRemoved(level, pos);\n        }\n    }\n\n    @Override\n    protected InteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {\n        /*\n            Wrench: sneak + right-click anywhere on cable to remove cable\n                    right-click on a cable side to disconnect on that side\n                    sneak + right-click on part to remove that part\n            No wrench: right-click to open GUI\n         */\n        BlockEntityMultipartTicking tile = IModHelpers.get().getBlockEntityHelpers().get(pLevel, pPos, BlockEntityMultipartTicking.class).orElse(null);\n        if(tile != null) {\n            BlockRayTraceResultComponent rayTraceResult = getSelectedShape(pState, pLevel, pPos, CollisionContext.of(pPlayer))\n                    .rayTrace(pPos, pPlayer);\n            if(rayTraceResult != null) {\n                InteractionResult actionResultType = rayTraceResult.getComponent().onBlockActivated(pState, pLevel, pPos, pPlayer, pHand, rayTraceResult);\n                if (actionResultType.consumesAction()) {\n                    return actionResultType;\n                }\n            }\n        }\n        return super.useItemOn(pStack, pState, pLevel, pPos, pPlayer, pHand, pHitResult);\n    }\n\n    @Override\n    public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {\n        super.onPlace(state, world, pos, oldState, isMoving);\n        if (!world.isClientSide() && !SKIP_NETWORK_INIT) {\n            ICableFakeable cableFakeable = CableHelpers.getCableFakeable(world, pos, null).orElse(null);\n            if (cableFakeable != null && cableFakeable.isRealCable()) {\n                CableHelpers.onCableAdded(world, pos);\n            }\n        }\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        super.setPlacedBy(world, pos, state, placer, itemStack);\n        if (!world.isClientSide()) {\n            CableHelpers.onCableAddedByPlayer(world, pos, placer);\n        }\n    }\n\n    @Override\n    public ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData, Player player) {\n        BlockRayTraceResultComponent rayTraceResult = getSelectedShape(state, level, pos, CollisionContext.of(player))\n                .rayTrace(pos, player);\n        if(rayTraceResult != null && level instanceof Level levelReal) {\n            return rayTraceResult.getComponent().getCloneItemStack(levelReal, pos);\n        }\n        return super.getCloneItemStack(level, pos, state, includeData, player);\n    }\n\n    @Override\n    protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @org.jetbrains.annotations.Nullable Orientation orientation, boolean movedByPiston) {\n        super.neighborChanged(state, level, pos, neighborBlock, orientation, movedByPiston);\n        NetworkHelpers.onElementProviderBlockNeighborChange(level, pos, orientation != null ? orientation.getFront().getOpposite() : null);\n    }\n\n    @Override\n    public void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbor) {\n        super.onNeighborChange(state, world, pos, neighbor);\n        if (world instanceof Level level) {\n            NetworkHelpers.onElementProviderBlockNeighborChange(level, pos, null);\n        }\n    }\n\n    @Override\n    public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rand) {\n        super.tick(state, world, pos, rand);\n        IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityMultipartTicking.class)\n                .ifPresent(tile -> {\n                    for (Map.Entry<Direction, PartHelpers.PartStateHolder<?, ?>> entry : tile\n                            .getPartContainer().getPartData().entrySet()) {\n                        updateTickPart(entry.getValue().getPart(), world, pos, entry.getValue().getState(), rand);\n                    }\n                });\n    }\n\n    protected void updateTickPart(IPartType partType, Level world, BlockPos pos, IPartState partState, RandomSource random) {\n        partType.updateTick(world, pos, partState, random);\n    }\n\n    /* --------------- Start shapes and rendering --------------- */\n\n    public AABB getCableBoundingBox(Direction side) {\n        if (side == null) {\n            return CABLE_CENTER_BOUNDINGBOX;\n        } else {\n            return CABLE_SIDE_BOUNDINGBOXES.get(side);\n        }\n    }\n\n    public VoxelShapeComponents getSelectedShape(BlockState blockState, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {\n        return voxelShapeComponentsFactory.createShape(blockState, world, pos, selectionContext);\n    }\n\n    private final Cache<String, VoxelShape> CACHE_COLLISION_SHAPES = CacheBuilder.newBuilder()\n            .expireAfterAccess(1, TimeUnit.MINUTES)\n            .build();\n\n    @Override\n    public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {\n        VoxelShapeComponents selectedShape = getSelectedShape(state, world, pos, selectionContext);\n        BlockRayTraceResultComponent rayTraceResult = selectedShape.rayTrace(pos, selectionContext instanceof EntityCollisionContext ? ((EntityCollisionContext) selectionContext).getEntity() : null);\n        if (rayTraceResult != null) {\n            return rayTraceResult.getComponent().getShape(state, world, pos, selectionContext);\n        }\n\n        String cableState = selectedShape.getStateId();\n\n        // Cache the operations below, as they are too expensive to execute each render tick\n        try {\n            return CACHE_COLLISION_SHAPES.get(cableState, () -> {\n            // Combine all VoxelShapes using IBooleanFunction.OR,\n            // because for some reason our VoxelShapeComponents aggregator does not handle collisions properly.\n            // This can probably be fixed, but I spent too much time on this already, and the current solution works just fine.\n            Iterator<VoxelShape> it = selectedShape.iterator();\n            if (!it.hasNext()) {\n                return Shapes.empty();\n            }\n            VoxelShape shape = it.next();\n            while (it.hasNext()) {\n                shape = Shapes.join(shape, it.next(), BooleanOp.OR);\n            }\n            return shape.optimize();\n            });\n        } catch (Exception e) {\n            return Helpers.sneakyThrow(e);\n        }\n    }\n\n    @Override\n    public VoxelShape getCollisionShape(BlockState blockState, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {\n        return (disableCollisionBox || GeneralConfig.disableCableCollision) ? Shapes.empty() : super.getCollisionShape(blockState, world, pos, selectionContext);\n    }\n\n    @Override\n    public boolean hasDynamicShape() {\n        return BlockCableConfig.dynamicShape;\n    }\n\n    @Override\n    public RenderShape getRenderShape(BlockState blockState) {\n        return RenderShape.MODEL;\n    }\n\n    @Override\n    protected VoxelShape getBlockSupportShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {\n        return this.getShape(pState, pLevel, pPos, new CollisionContextBlockSupport());\n    }\n\n    @Override\n    public boolean shouldDisplayFluidOverlay(BlockState state, BlockAndLightGetter level, BlockPos pos, FluidState fluidState) {\n        return level instanceof RenderSectionRegion renderSectionRegion && CableHelpers.getFacade(renderSectionRegion.level, pos, state).isPresent();\n    }\n\n    @Override\n    public BlockState getAppearance(BlockState state, BlockAndLightGetter level, BlockPos pos, Direction side, @Nullable BlockState queryState, @Nullable BlockPos queryPos) {\n        if (level instanceof ILevelExtension levelExtension) {\n            return CableHelpers.getFacade(levelExtension, pos, state).orElseGet(() -> super.getAppearance(state, level, pos, side, queryState, queryPos));\n        }\n        return super.getAppearance(state, level, pos, side, queryState, queryPos);\n    }\n\n    /* --------------- Start IDynamicRedstone --------------- */\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public boolean isSignalSource(BlockState blockState) {\n        return true;\n    }\n\n    @Override\n    public boolean canConnectRedstone(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {\n        if (world instanceof ILevelExtension levelExtension) {\n            if (side == null) {\n                for (Direction dummySide : Direction.values()) {\n                    IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, dummySide, Capabilities.DynamicRedstone.BLOCK).orElse(null);\n                    if (dynamicRedstone != null && (dynamicRedstone.getRedstoneLevel() >= 0 || dynamicRedstone.isAllowRedstoneInput())) {\n                        return true;\n                    }\n                }\n                return false;\n            }\n            IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);\n            return dynamicRedstone != null && (dynamicRedstone.getRedstoneLevel() >= 0 || dynamicRedstone.isAllowRedstoneInput());\n        }\n        return false;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public int getDirectSignal(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {\n        if (world instanceof ILevelExtension levelExtension) {\n            IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);\n            return dynamicRedstone != null && dynamicRedstone.isDirect() ? dynamicRedstone.getRedstoneLevel() : 0;\n        }\n        return 0;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public int getSignal(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {\n        if (world instanceof ILevelExtension levelExtension) {\n            IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);\n            return dynamicRedstone != null ? dynamicRedstone.getRedstoneLevel() : 0;\n        }\n        return 0;\n    }\n\n    /* --------------- Start IDynamicLight --------------- */\n\n    @Override\n    public int getLightEmission(BlockState blockState, BlockGetter world, BlockPos pos) {\n        int light = 0;\n        if (world instanceof ILevelExtension levelExtension) {\n            for (Direction side : Direction.values()) {\n                IDynamicLight dynamicLight = levelExtension.getCapability(Capabilities.DynamicLight.BLOCK, pos, blockState, null, side);\n                if (dynamicLight != null) {\n                    light = Math.max(light, dynamicLight.getLightLevel());\n                }\n            }\n        }\n        return light;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCableClientConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.color.block.BlockTintSource;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.particle.ParticleEngine;\nimport net.minecraft.client.renderer.block.BlockAndTintGetter;\nimport net.minecraft.client.renderer.block.dispatch.BlockStateModel;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.client.renderer.texture.TextureAtlas;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.HitResult;\nimport net.neoforged.neoforge.client.event.RegisterColorHandlersEvent;\nimport net.neoforged.neoforge.client.event.RegisterItemModelsEvent;\nimport net.neoforged.neoforge.client.event.TextureAtlasStitchedEvent;\nimport net.neoforged.neoforge.client.extensions.common.IClientBlockExtensions;\nimport net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.client.model.IDynamicModelElementCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.client.model.CableModel;\nimport org.cyclops.integrateddynamics.core.client.model.ItemModelCable;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\n\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\npublic class BlockCableClientConfig extends BlockClientConfig<IntegratedDynamics> {\n\n    public static TextureAtlasSprite BLOCK_TEXTURE;\n\n    public BlockCableClientConfig(BlockConfigCommon<IntegratedDynamics> blockConfig) {\n        super(blockConfig);\n        blockConfig.getMod().getModEventBus().addListener(this::onRegisterColors);\n        blockConfig.getMod().getModEventBus().addListener(this::registerClientExtensions);\n        blockConfig.getMod().getModEventBus().addListener(this::postTextureStitch);\n        blockConfig.getMod().getModEventBus().addListener((RegisterItemModelsEvent event) -> event.register(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"cable\"), ItemModelCable.Unbaked.MAP_CODEC));\n    }\n\n    public void onRegisterColors(RegisterColorHandlersEvent.BlockTintSources event) {\n        event.register(List.of(new BlockCableClientConfig.BlockColor()), getBlockConfig().getInstance());\n    }\n\n    public void postTextureStitch(TextureAtlasStitchedEvent event) {\n        if (event.getAtlas().location().equals(TextureAtlas.LOCATION_BLOCKS)) {\n            BLOCK_TEXTURE = event.getAtlas().getSprite(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"block/cable\"));\n        }\n    }\n\n    public void registerClientExtensions(RegisterClientExtensionsEvent event) {\n        event.registerBlock(new IClientBlockExtensions() {\n            @Override\n            public boolean addHitEffects(BlockState blockState, Level world, HitResult target, ParticleEngine particleManager) {\n                BlockPos blockPos = ((BlockHitResult) target).getBlockPos();\n                if(CableHelpers.hasFacade(world, blockPos, blockState)) {\n                    CableHelpers.getFacade(world, blockPos, blockState)\n                            .ifPresent(facadeState -> IModHelpers.get().getRenderHelpers().addBlockHitEffects(particleManager, (ClientLevel) world, facadeState, blockPos, ((BlockHitResult) target).getDirection()));\n                    return true;\n                } else {\n                    return false;\n                }\n            }\n        }, getBlockConfig().getInstance());\n    }\n\n    @Override\n    public IDynamicModelElementCommon getDynamicModelElement() {\n        return new DynamicModel();\n    }\n\n    public static class BlockColor implements BlockTintSource {\n        @Override\n        public int color(BlockState blockState) {\n            return -1;\n        }\n\n        @Override\n        public int colorInWorld(BlockState blockState, BlockAndTintGetter world, BlockPos blockPos) {\n            return CableHelpers.getFacadeMultipartTicking(world, blockPos)\n                    .map(facadeState -> {\n                        BlockTintSource tintSource = Minecraft.getInstance().getBlockColors().getTintSource(facadeState, 0);\n                        if (tintSource != null) {\n                            return tintSource.colorInWorld(facadeState, world, blockPos);\n                        }\n                        return -1;\n                    })\n                    .orElse(-1);\n        }\n    }\n\n    public static class DynamicModel implements IDynamicModelElementCommon {\n        @Override\n        public BlockStateModel createDynamicBlockModel(Consumer<Pair<BlockState, BlockStateModel>> modelConsumer, Function<BlockState, BlockStateModel> modelRetriever) {\n            CableModel model = new CableModel();\n            modelConsumer.accept(Pair.of(RegistryEntries.BLOCK_CABLE.get().defaultBlockState(), model));\n            modelConsumer.accept(Pair.of(RegistryEntries.BLOCK_CABLE.get().defaultBlockState().setValue(BlockCable.WATERLOGGED, true), model));\n            return model;\n        }\n\n        @Override\n        public ItemModel createDynamicItemModel(Consumer<Pair<Identifier, ItemModel>> modelConsumer, Function<Identifier, ItemModel> modelRetriever) {\n            ItemModelCable model = new ItemModelCable(new CableModel());\n            Identifier registryName = BuiltInRegistries.BLOCK.getKey(RegistryEntries.BLOCK_CABLE.get());\n            modelConsumer.accept(Pair.of(registryName, model));\n            return model;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCableConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.item.ItemBlockCable;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Config for {@link BlockCable}.\n * @author rubensworks\n */\npublic class BlockCableConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"If cable shapes should be determined dynamically. Disable this if FPS issues would occur.\", minimalValue = 0)\n    public static boolean dynamicShape = true;\n\n    public BlockCableConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"cable\",\n                (eConfig, properties) -> new BlockCable(properties\n                        .strength(BlockCable.BLOCK_HARDNESS)\n                        .forceSolidOn()\n                        .sound(SoundType.METAL)\n                        .isRedstoneConductor((blockState, world, pos) -> false)),\n                (eConfig, block) -> new ItemBlockCable(block, eConfig.createDefaultItemProperties())\n        );\n    }\n\n    @Override\n    @Nullable\n    public BlockClientConfig<IntegratedDynamics> constructBlockClientConfig() {\n        if (getMod().getModHelpers().getMinecraftHelpers().isClientSide()) {\n            return new BlockCableClientConfig(this);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCoalGenerator.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.LivingEntity;\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.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.BooleanProperty;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityCoalGenerator;\nimport org.cyclops.integrateddynamics.core.block.BlockWithEntityGuiCabled;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can generate energy from coal.\n * @author rubensworks\n */\npublic class BlockCoalGenerator extends BlockWithEntityGuiCabled {\n\n    public static final MapCodec<BlockCoalGenerator> CODEC = simpleCodec(BlockCoalGenerator::new);\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.HORIZONTAL_FACING;\n    public static final BooleanProperty LIT = BlockStateProperties.LIT;\n\n    public BlockCoalGenerator(Properties properties) {\n        super(properties, BlockEntityCoalGenerator::new);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(FACING, Direction.NORTH)\n                .setValue(LIT, false));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_COAL_GENERATOR.get(), new BlockEntityCoalGenerator.Ticker());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING, LIT);\n    }\n\n    @Override\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        super.setPlacedBy(world, pos, state, placer, itemStack);\n        IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityCoalGenerator.class)\n                .ifPresent(BlockEntityCoalGenerator::updateBlockState);\n    }\n\n    @Override\n    protected boolean isPickBlockPersistData() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCoalGeneratorConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockCoalGenerator}.\n * @author rubensworks\n */\npublic class BlockCoalGeneratorConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCoalGeneratorConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"coal_generator\",\n                (eConfig, properties) -> new BlockCoalGenerator(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCreativeEnergyBattery.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.Block;\n\n/**\n * A block that can hold defined variables so that they can be referred to elsewhere in the network.\n *\n * @author rubensworks\n */\npublic class BlockCreativeEnergyBattery extends BlockEnergyBatteryBase {\n\n    public static final MapCodec<BlockCreativeEnergyBattery> CODEC = simpleCodec(BlockCreativeEnergyBattery::new);\n\n    public BlockCreativeEnergyBattery(Block.Properties properties) {\n        super(properties);\n    }\n\n    public boolean isCreative() {\n        return true;\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCreativeEnergyBatteryConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.world.item.CreativeModeTab;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.SoundType;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;\nimport net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.capability.energystorage.IEnergyStorageCapacity;\nimport org.cyclops.integrateddynamics.core.item.ItemBlockEnergyContainer;\nimport org.cyclops.integrateddynamics.core.item.ItemBlockEnergyContainerAutoSupply;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Config for {@link BlockCreativeEnergyBattery}.\n * @author rubensworks\n */\npublic class BlockCreativeEnergyBatteryConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCreativeEnergyBatteryConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"energy_battery_creative\",\n                (eConfig, properties) -> new BlockCreativeEnergyBattery(properties\n                        .sound(SoundType.METAL)\n                        .strength(2.0F, 5.0F)),\n                (eConfig, block) -> new ItemBlockEnergyContainerAutoSupply(block,\n                        eConfig.createDefaultItemProperties()\n                )\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(this::registerCapability);\n        IntegratedDynamics._instance.getModEventBus().addListener(this::fillCreativeTab);\n    }\n\n    protected void registerCapability(RegisterCapabilitiesEvent event) {\n        event.registerItem(Capabilities.Energy.ITEM, (stack, context) -> ((ItemBlockEnergyContainer) getItemInstance()).createCapability(stack, context), getInstance());\n    }\n\n    protected void fillCreativeTab(BuildCreativeModeTabContentsEvent event) {\n        if (event.getTab() == IntegratedDynamics._instance.getDefaultCreativeTab()) {\n            for (ItemStack itemStack : dynamicCreativeTabEntries()) {\n                event.accept(itemStack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS);\n            }\n        }\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        // Register items dynamically into tab, because when this is called, capabilities are not initialized yet.\n        return Collections.emptyList();\n    }\n\n    protected Collection<ItemStack> dynamicCreativeTabEntries() {\n        List<ItemStack> itemStacks = Lists.newArrayList();\n        ItemStack full = new ItemStack(getInstance());\n        IEnergyStorageCapacity energyStorage = (IEnergyStorageCapacity) ((ItemBlockEnergyContainer) full.getItem()).getEnergyBattery(full).orElse(null);\n        ((BlockCreativeEnergyBattery) getInstance()).fill(energyStorage);\n        itemStacks.add(full);\n        return itemStacks;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCreativeEnergyBatteryConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.neoforged.neoforge.client.event.RegisterSpecialModelRendererEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.client.render.blockentity.ItemStackBlockEntityEnergyBatteryRender;\n\n/**\n * @author rubensworks\n */\npublic class BlockCreativeEnergyBatteryConfigClient extends BlockClientConfig<IntegratedDynamics> {\n    public BlockCreativeEnergyBatteryConfigClient(BlockConfigCommon<IntegratedDynamics> blockConfig) {\n        super(blockConfig);\n        blockConfig.getMod().getModEventBus().addListener((RegisterSpecialModelRendererEvent event) -> event.register(blockConfig.getResourceKey().identifier(), ItemStackBlockEntityEnergyBatteryRender.Unbaked.MAP_CODEC));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedChorusBlockConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Chorus block.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedChorusBlockConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedChorusBlockConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_block\",\n                (eConfig, properties) -> new Block(properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedChorusBlockSlabConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SlabBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Chorus Block Slab.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedChorusBlockSlabConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedChorusBlockSlabConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_block_slab\",\n                (eConfig, properties) -> new SlabBlock(properties\n                        .mapColor(MapColor.COLOR_PINK)\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedChorusBlockStairsConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.StairBlock;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Crystalized Chorus Stairs.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedChorusBlockStairsConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedChorusBlockStairsConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_block_stairs\",\n                (eConfig, properties) -> new StairBlock(RegistryEntries.BLOCK_CRYSTALIZED_CHORUS_BLOCK.get().defaultBlockState(), properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedChorusBrickConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Chorus block.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedChorusBrickConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedChorusBrickConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_brick\",\n                (eConfig, properties) -> new Block(properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedChorusBrickSlabConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SlabBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Chorus Brick Slab.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedChorusBrickSlabConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedChorusBrickSlabConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_brick_slab\",\n                (eConfig, properties) -> new SlabBlock(properties\n                        .mapColor(MapColor.COLOR_PINK)\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedChorusBrickStairsConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.StairBlock;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Crystalized Chorus Stairs.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedChorusBrickStairsConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedChorusBrickStairsConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_brick_stairs\",\n                (eConfig, properties) -> new StairBlock(RegistryEntries.BLOCK_CRYSTALIZED_CHORUS_BRICK.get().defaultBlockState(), properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedMenrilBlockConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Menril block.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedMenrilBlockConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedMenrilBlockConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_block\",\n                (eConfig, properties) -> new Block(properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedMenrilBlockSlabConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SlabBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Menril Block Slab.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedMenrilBlockSlabConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedMenrilBlockSlabConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_block_slab\",\n                (eConfig, properties) -> new SlabBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedMenrilBlockStairsConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.StairBlock;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Crystallized Menril Block Stairs.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedMenrilBlockStairsConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedMenrilBlockStairsConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_block_stairs\",\n                (eConfig, properties) -> new StairBlock(RegistryEntries.BLOCK_CRYSTALIZED_MENRIL_BLOCK.get().defaultBlockState(), properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedMenrilBrickConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Menril block.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedMenrilBrickConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedMenrilBrickConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_brick\",\n                (eConfig, properties) -> new Block(properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedMenrilBrickSlabConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SlabBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Menril Brick Slab.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedMenrilBrickSlabConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedMenrilBrickSlabConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_brick_slab\",\n                (eConfig, properties) -> new SlabBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockCrystalizedMenrilBrickStairsConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.StairBlock;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Crystallized Menril Brick Stairs.\n * @author rubensworks\n *\n */\npublic class BlockCrystalizedMenrilBrickStairsConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockCrystalizedMenrilBrickStairsConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_brick_stairs\",\n                (eConfig, properties) -> new StairBlock(RegistryEntries.BLOCK_CRYSTALIZED_MENRIL_BRICK.get().defaultBlockState(), properties\n                        .sound(SoundType.SNOW)\n                        .strength(1.5F)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockDelay.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.LivingEntity;\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.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDelay;\nimport org.cyclops.integrateddynamics.core.block.BlockWithEntityGuiCabled;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can delay variables.\n * @author rubensworks\n */\npublic class BlockDelay extends BlockWithEntityGuiCabled {\n\n    public static final MapCodec<BlockDelay> CODEC = simpleCodec(BlockDelay::new);\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.HORIZONTAL_FACING;\n\n    public BlockDelay(Properties properties) {\n        super(properties, BlockEntityDelay::new);\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(FACING, Direction.NORTH));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_DELAY.get(), new BlockEntityDelay.Ticker());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING);\n    }\n\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos blockPos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        if (!world.isClientSide()) {\n            IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityDelay.class)\n                    .ifPresent(tile -> {\n                        if (itemStack.has(RegistryEntries.DATACOMPONENT_PROXY_ID)) {\n                            tile.setProxyId(itemStack.get(RegistryEntries.DATACOMPONENT_PROXY_ID));\n                        } else {\n                            tile.generateNewProxyId();\n                        }\n                        tile.setChanged();\n                    });\n        }\n        super.setPlacedBy(world, blockPos, state, placer, itemStack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockDelayConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.item.ItemBlockDelay;\n\n/**\n * Config for {@link BlockDelay}.\n * @author rubensworks\n */\npublic class BlockDelayConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The maximum value history length that can be maintained..\", minimalValue = 1)\n    public static int maxHistoryCapacity = 1024;\n\n    public BlockDelayConfig() {\n        super(\n            IntegratedDynamics._instance,\n            \"delay\",\n                (eConfig, properties) -> new BlockDelay(properties\n                    .strength(2.0F, 5.0F)\n                    .sound(SoundType.METAL)),\n                (eConfig, block) -> new ItemBlockDelay(block, eConfig.createDefaultItemProperties())\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.Containers;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BaseEntityBlock;\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.phys.BlockHitResult;\nimport net.minecraft.world.phys.shapes.BooleanOp;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport org.cyclops.cyclopscore.block.BlockWithEntityGui;\nimport org.cyclops.cyclopscore.fluid.SingleUseTank;\nimport org.cyclops.cyclopscore.helper.IFluidHelpersNeoForge;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDryingBasin;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block for drying stuff.\n * @author rubensworks\n */\npublic class BlockDryingBasin extends BlockWithEntityGui {\n\n    public static final MapCodec<BlockDryingBasin> CODEC = simpleCodec(BlockDryingBasin::new);\n    private static final VoxelShape SHAPE_RAYTRACE = box(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D);\n    private static final VoxelShape SHAPE = Shapes.join(Shapes.block(), Shapes.or(\n            box(0.0D, 0.0D, 4.0D, 16.0D, 3.0D, 12.0D),\n            new VoxelShape[]{\n                    box(4.0D, 0.0D, 0.0D, 12.0D, 3.0D, 16.0D),\n                    box(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D),\n                    SHAPE_RAYTRACE\n            }), BooleanOp.ONLY_FIRST);\n\n    public BlockDryingBasin(Properties properties) {\n        super(properties, BlockEntityDryingBasin::new);\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_DRYING_BASIN.get(), level.isClientSide() ? new BlockEntityDryingBasin.TickerClient() : new BlockEntityDryingBasin.TickerServer());\n    }\n\n    @Override\n    public InteractionResult useWithoutItem(BlockState blockState, Level world, BlockPos blockPos, Player player,\n                                             BlockHitResult rayTraceResult) {\n        return IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityDryingBasin.class)\n                .map(tile -> {\n                    IFluidHelpersNeoForge fh = IModHelpersNeoForge.get().getFluidHelpers();\n                    ItemAccess itemAccess = ItemAccess.forPlayerSlot(player, player.getInventory().getSelectedSlot());\n                    ResourceHandler<FluidResource> itemFluidHandler = itemAccess.getCapability(Capabilities.Fluid.ITEM);\n                    SingleUseTank tank = tile.getTank();\n                    ItemStack tileStack = tile.getInventory().getItem(0);\n\n                    if (itemAccess.getAmount() == 0 && !tileStack.isEmpty()) {\n                        player.getInventory().setItem(player.getInventory().getSelectedSlot(), tileStack);\n                        tile.getInventory().setItem(0, ItemStack.EMPTY);\n                        tile.sendUpdate();\n                        return InteractionResult.SUCCESS;\n                    } else if(player.getInventory().add(tileStack)){\n                        tile.getInventory().setItem(0, ItemStack.EMPTY);\n                        tile.sendUpdate();\n                        return InteractionResult.SUCCESS;\n                    } else if (itemFluidHandler != null && !tank.isFull() && fh.canExtract(itemFluidHandler)) {\n                        fh.move(itemFluidHandler, tank, Integer.MAX_VALUE, player, true, false);\n                        return InteractionResult.SUCCESS;\n                    } else if (itemFluidHandler != null && !tank.isEmpty() && fh.canInsert(itemFluidHandler, tank.getFluid())) {\n                        fh.move(tank, itemFluidHandler, Integer.MAX_VALUE, player, false, false);\n                        return InteractionResult.SUCCESS;\n                    } else if (itemAccess.getAmount() > 0 && tileStack.isEmpty()) {\n                        tile.getInventory().setItem(0, itemAccess.getResource().toStack());\n                        player.getInventory().getItem(player.getInventory().getSelectedSlot()).shrink(1);\n                        tile.sendUpdate();\n                        return InteractionResult.SUCCESS;\n                    }\n                    return InteractionResult.PASS;\n                })\n                .orElse(InteractionResult.PASS);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public boolean hasAnalogOutputSignal(BlockState blockState) {\n        return true;\n    }\n\n    @Override\n    protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos, Direction direction) {\n        return IModHelpers.get().getBlockEntityHelpers().get(level, pos, BlockEntityDryingBasin.class)\n                .map(tile -> tile.getInventory().getItem(0) != null ? 15 : 0)\n                .orElse(0);\n    }\n\n    @Override\n    public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        return SHAPE;\n    }\n\n    @Override\n    public VoxelShape getInteractionShape(BlockState blockState, BlockGetter world, BlockPos blockPos) {\n        return SHAPE_RAYTRACE;\n    }\n\n    @Override\n    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {\n        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);\n        Containers.updateNeighboursAfterDestroy(state, level, pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockDryingBasin}.\n * @author rubensworks\n */\npublic class BlockDryingBasinConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"drying_basin\",\n                (eConfig, properties) -> new BlockDryingBasin(properties\n                        .strength(2.0F, 5.0F)\n                        .noOcclusion()\n                        .isRedstoneConductor((blockState, world, pos) -> false)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockEnergyBattery.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.Block;\n\n/**\n * A block that can hold defined variables so that they can be referred to elsewhere in the network.\n *\n * @author rubensworks\n */\npublic class BlockEnergyBattery extends BlockEnergyBatteryBase {\n\n    public static final MapCodec<BlockEnergyBattery> CODEC = simpleCodec(BlockEnergyBattery::new);\n\n    public BlockEnergyBattery(Block.Properties properties) {\n        super(properties);\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    public boolean isCreative() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockEnergyBatteryBase.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.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.phys.BlockHitResult;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.core.block.BlockContainerCabled;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * A block that holds energy.\n *\n * @author rubensworks\n */\npublic abstract class BlockEnergyBatteryBase extends BlockContainerCabled {\n\n    public BlockEnergyBatteryBase(Block.Properties properties) {\n        super(properties, BlockEntityEnergyBattery::new);\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_ENERGY_BATTERY.get(), new BlockEntityEnergyBattery.Ticker());\n    }\n\n    public abstract boolean isCreative();\n\n    @Override\n    public InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player,\n                                             BlockHitResult blockRayTraceResult) {\n        InteractionResult superActionResult = super.useWithoutItem(state, world, pos, player, blockRayTraceResult);\n        if (superActionResult.consumesAction()) {\n            return superActionResult;\n        }\n\n        if (player.getItemInHand(player.getUsedItemHand()).isEmpty()) {\n            return IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityEnergyBattery.class)\n                    .<InteractionResult>map(tile -> {\n                        player.sendOverlayMessage(Helpers.getLocalizedEnergyLevel(\n                                tile.getEnergyStored(), tile.getMaxEnergyStored()));\n                        return InteractionResult.SUCCESS;\n                    })\n                    .orElse(InteractionResult.PASS);\n        }\n\n        return InteractionResult.PASS;\n    }\n\n    /**\n     * Fill an EnergyHandler with all the energy it can hold\n     * @param energyStorage EnergyHandler that is to be filled\n     */\n    public static void fill(EnergyHandler energyStorage){\n        int max = energyStorage.getCapacityAsInt();\n        int stored = 1;\n        while (stored > 0) {\n            try (var tx = Transaction.openRoot()) {\n                stored = energyStorage.insert(max, tx);\n                tx.commit();\n            }\n        }\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos blockPos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        if (!world.isClientSide()) {\n            IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityEnergyBattery.class)\n                    .ifPresent(tile -> itemStackToTile(itemStack.copy().split(1), tile));\n        }\n        super.setPlacedBy(world, blockPos, state, placer, itemStack);\n    }\n\n    public static void itemStackToTile(ItemStack itemStack, BlockEntityEnergyBattery tile) {\n        if (itemStack == null || itemStack.isEmpty()) return;\n        Optional.ofNullable(itemStack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(itemStack)))\n                .ifPresent(energyStorage -> {\n                    tile.setEnergyStored(energyStorage.getAmountAsInt());\n                    tile.getEnergyHandler().setCapacity(energyStorage.getCapacityAsInt());\n                });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockEnergyBatteryConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.world.item.CreativeModeTab;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.SoundType;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;\nimport net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.capability.energystorage.IEnergyStorageCapacity;\nimport org.cyclops.integrateddynamics.core.item.ItemBlockEnergyContainer;\nimport org.cyclops.integrateddynamics.core.item.ItemBlockEnergyContainerAutoSupply;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Config for {@link BlockEnergyBattery}.\n * @author rubensworks\n */\npublic class BlockEnergyBatteryConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The default capacity of an energy battery.\", minimalValue = 0)\n    public static int capacity = 1000000;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The 1/X fraction of the battery capacity that is allowed to be transfered per tick.\", isCommandable = true, minimalValue = 0)\n    public static int energyRateCapacityFraction = 2000;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The minimum energy transfer rate per tick.\", isCommandable = true, minimalValue = 0)\n    public static int minEnergyRate = 2000;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The maximum capacity shown in creative tabs. (Make sure that you do not cross the max int size)\")\n    public static int maxCreativeTabCapacity = 655360000;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The maximum capacity visible in the creative tabs. (Make sure that you do not cross the max int size)\")\n    public static int maxCreativeCapacity = 40960000;\n\n    public BlockEnergyBatteryConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"energy_battery\",\n                (eConfig, properties) -> new BlockEnergyBattery(properties\n                        .sound(SoundType.METAL)\n                        .strength(2.0F, 5.0F)),\n                (eConfig, block) -> new ItemBlockEnergyContainerAutoSupply(block, eConfig.createDefaultItemProperties())\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(this::registerCapability);\n        IntegratedDynamics._instance.getModEventBus().addListener(this::fillCreativeTab);\n    }\n    protected void registerCapability(RegisterCapabilitiesEvent event) {\n        event.registerItem(Capabilities.Energy.ITEM, (stack, context) -> ((ItemBlockEnergyContainer) getItemInstance()).createCapability(stack, context), getInstance());\n    }\n\n    protected void fillCreativeTab(BuildCreativeModeTabContentsEvent event) {\n        if (event.getTab() == IntegratedDynamics._instance.getDefaultCreativeTab()) {\n            for (ItemStack itemStack : dynamicCreativeTabEntries()) {\n                event.accept(itemStack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS);\n            }\n        }\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        // Register items dynamically into tab, because when this is called, capabilities are not initialized yet.\n        return Collections.emptyList();\n    }\n\n    protected Collection<ItemStack> dynamicCreativeTabEntries() {\n        List<ItemStack> itemStacks = Lists.newArrayList();\n\n        ItemStack itemStack = new ItemStack(getInstance());\n\n        int capacityOriginal = BlockEnergyBatteryConfig.capacity;\n        int capacity = capacityOriginal;\n        int lastCapacity;\n        do {\n            ItemStack currentStack = itemStack.copy();\n            IEnergyStorageCapacity energyStorage = (IEnergyStorageCapacity) ((ItemBlockEnergyContainer) currentStack.getItem()).getEnergyBattery(currentStack).orElse(null);\n            energyStorage.setCapacity(capacity);\n            itemStacks.add(currentStack.copy());\n            ((BlockEnergyBattery) getInstance()).fill(energyStorage);\n            itemStacks.add(currentStack.copy());\n            lastCapacity = capacity;\n            capacity = capacity << 2;\n        } while (capacity < Math.min(BlockEnergyBatteryConfig.maxCreativeCapacity, BlockEnergyBatteryConfig.maxCreativeTabCapacity) && capacity > lastCapacity);\n\n        return itemStacks;\n    }\n\n    @Override\n    public @Nullable BlockClientConfig<IntegratedDynamics> constructBlockClientConfig() {\n        return new BlockEnergyBatteryConfigClient(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockEnergyBatteryConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.neoforged.neoforge.client.event.RegisterSpecialModelRendererEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.client.render.blockentity.ItemStackBlockEntityEnergyBatteryRender;\n\n/**\n * @author rubensworks\n */\npublic class BlockEnergyBatteryConfigClient extends BlockClientConfig<IntegratedDynamics> {\n    public BlockEnergyBatteryConfigClient(BlockConfigCommon<IntegratedDynamics> blockConfig) {\n        super(blockConfig);\n        blockConfig.getMod().getModEventBus().addListener((RegisterSpecialModelRendererEvent event) -> event.register(blockConfig.getResourceKey().identifier(), ItemStackBlockEntityEnergyBatteryRender.Unbaked.MAP_CODEC));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockFluidLiquidChorus.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.sounds.SoundEvents;\nimport net.minecraft.sounds.SoundSource;\nimport net.minecraft.util.Mth;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.InsideBlockEffectApplier;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.LiquidBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * A block for the Liquid Chorus fluid.\n * @author rubensworks\n */\npublic class BlockFluidLiquidChorus extends LiquidBlock {\n\n    public BlockFluidLiquidChorus(Block.Properties builder) {\n        super(RegistryEntries.FLUID_LIQUID_CHORUS.get(), builder);\n    }\n\n    @Override\n    protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean intersects) {\n        super.entityInside(state, level, pos, entity, effectApplier, intersects);\n\n        // Simulate chorus-eating\n        if (entity instanceof LivingEntity) {\n            LivingEntity entityLiving = (LivingEntity) entity;\n            double d0 = entityLiving.getX();\n            double d1 = entityLiving.getY();\n            double d2 = entityLiving.getZ();\n\n            for (int i = 0; i < 16; ++i) {\n                double d3 = entityLiving.getX() + (entityLiving.getRandom().nextDouble() - 0.5D) * 16.0D;\n                double d4 = Mth.clamp(entityLiving.getY() + (double) (entityLiving.getRandom().nextInt(16) - 8), 0.0D, level.getMaxY());\n                double d5 = entityLiving.getZ() + (entityLiving.getRandom().nextDouble() - 0.5D) * 16.0D;\n\n                if (entityLiving.isPassenger()) {\n                    entityLiving.stopRiding();\n                }\n\n                if (entityLiving.randomTeleport(d3, d4, d5, true)) {\n                    level.playSound(null, d0, d1, d2, SoundEvents.CHORUS_FRUIT_TELEPORT, SoundSource.PLAYERS, 1.0F, 1.0F);\n                    entityLiving.playSound(SoundEvents.CHORUS_FRUIT_TELEPORT, 1.0F, 1.0F);\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockFluidLiquidChorusConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.material.PushReaction;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * Config for {@link BlockFluidLiquidChorus}.\n * @author rubensworks\n *\n */\npublic class BlockFluidLiquidChorusConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockFluidLiquidChorusConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"block_liquid_chorus\",\n                (eConfig, properties) -> new BlockFluidLiquidChorus(properties\n                        .replaceable()\n                        .noCollision()\n                        .strength(100.0F)\n                        .pushReaction(PushReaction.DESTROY)\n                        .noLootTable()\n                        .liquid()),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        return Collections.emptyList();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockFluidMenrilResin.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.LiquidBlock;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * A block for the Menril Resin fluid.\n * @author rubensworks\n */\npublic class BlockFluidMenrilResin extends LiquidBlock {\n\n    public BlockFluidMenrilResin(Block.Properties builder) {\n        super(RegistryEntries.FLUID_MENRIL_RESIN.get(), builder);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockFluidMenrilResinConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.material.PushReaction;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * Config for {@link BlockFluidMenrilResin}.\n * @author rubensworks\n *\n */\npublic class BlockFluidMenrilResinConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockFluidMenrilResinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"block_menril_resin\",\n                (eConfig, properties) -> new BlockFluidMenrilResin(properties\n                        .replaceable()\n                        .noCollision()\n                        .strength(100.0F)\n                        .pushReaction(PushReaction.DESTROY)\n                        .noLootTable()\n                        .liquid()),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        return Collections.emptyList();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockInvisibleLight.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.RenderShape;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.IntegerProperty;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\n\n/**\n * An invisible light source with variable intensity.\n * @author rubensworks\n */\npublic class BlockInvisibleLight extends Block {\n\n    public static final IntegerProperty LIGHT = IntegerProperty.create(\"light\", 0, 15);\n\n    public BlockInvisibleLight(Properties properties) {\n        super(properties);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(LIGHT, 0));\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(LIGHT);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public RenderShape getRenderShape(BlockState blockState) {\n        return RenderShape.INVISIBLE;\n    }\n\n    @Override\n    public boolean canBeReplaced(BlockState blockState, BlockPlaceContext blockItemUseContext) {\n        return true;\n    }\n\n    @Override\n    public VoxelShape getShape(BlockState p_220053_1_, BlockGetter p_220053_2_, BlockPos p_220053_3_, CollisionContext p_220053_4_) {\n        return Shapes.empty();\n    }\n\n    @Override\n    public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) {\n        if (state.getBlock() == this) {\n            return state.getValue(LIGHT);\n        }\n        return super.getLightEmission(state, world, pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockInvisibleLightConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.material.PushReaction;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.ModConfigLocation;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * Config for {@link BlockInvisibleLight}.\n * @author rubensworks\n */\npublic class BlockInvisibleLightConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"If invisible light should act as full a block\", configLocation = ModConfigLocation.SERVER)\n    public static boolean invisibleLightBlock = true;\n\n    public BlockInvisibleLightConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"invisible_light\",\n                (eConfig, properties) -> new BlockInvisibleLight(properties\n                        .strength(3.0F)\n                        .sound(SoundType.METAL)\n                        .lightLevel((blockState) -> 15)\n                        .pushReaction(PushReaction.DESTROY)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockLogicProgrammer.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.SimpleMenuProvider;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.inventory.MenuConstructor;\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.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.EnumProperty;\nimport org.cyclops.cyclopscore.block.BlockGui;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammer;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can hold defined variables so that they can be referred to elsewhere in the network.\n *\n * @author rubensworks\n */\npublic class BlockLogicProgrammer extends BlockGui {\n\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.HORIZONTAL_FACING;\n\n    public BlockLogicProgrammer(Properties properties) {\n        super(properties);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                        .setValue(FACING, Direction.NORTH));\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING);\n    }\n\n    @Nullable\n    @Override\n    public BlockState getStateForPlacement(BlockPlaceContext blockItemUseContext) {\n        return this.defaultBlockState().setValue(FACING, blockItemUseContext.getPlayer().getDirection().getOpposite());\n    }\n\n    @Nullable\n    @Override\n    public MenuProvider getMenuProvider(BlockState blockState, Level world, BlockPos blockPos) {\n        return new SimpleMenuProvider(new MenuConstructor() {\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                return new ContainerLogicProgrammer(id, playerInventory);\n            }\n        }, Component.translatable(\"block.integrateddynamics.logic_programmer\"));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockLogicProgrammerConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockLogicProgrammer}.\n * @author rubensworks\n */\npublic class BlockLogicProgrammerConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockLogicProgrammerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"logic_programmer\",\n                (eConfig, properties) -> new BlockLogicProgrammer(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMaterializer.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMaterializer;\nimport org.cyclops.integrateddynamics.core.block.BlockWithEntityGuiCabled;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can materialize any variable to its raw value.\n * @author rubensworks\n */\npublic class BlockMaterializer extends BlockWithEntityGuiCabled {\n\n    public static final MapCodec<BlockMaterializer> CODEC = simpleCodec(BlockMaterializer::new);\n\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.HORIZONTAL_FACING;\n\n    public BlockMaterializer(Properties properties) {\n        super(properties, BlockEntityMaterializer::new);\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(FACING, Direction.NORTH));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_MATERIALIZER.get(), new BlockEntityMaterializer.Ticker());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING);\n    }\n\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMaterializerConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockMaterializer}.\n * @author rubensworks\n */\npublic class BlockMaterializerConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMaterializerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"materializer\",\n                (eConfig, properties) -> new BlockMaterializer(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.BooleanProperty;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.neoforged.neoforge.transfer.fluid.FluidUtil;\nimport net.neoforged.neoforge.fluids.SimpleFluidContent;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalDryingBasin;\nimport org.cyclops.integrateddynamics.core.block.BlockMechanicalMachine;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can expose variables.\n * @author rubensworks\n */\npublic class BlockMechanicalDryingBasin extends BlockMechanicalMachine {\n\n    public static final MapCodec<BlockMechanicalDryingBasin> CODEC = simpleCodec(BlockMechanicalDryingBasin::new);\n\n    public static final BooleanProperty LIT = BlockStateProperties.LIT;\n\n    public BlockMechanicalDryingBasin(Properties properties) {\n        super(properties, BlockEntityMechanicalDryingBasin::new);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(LIT, false));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_MECHANICAL_DRYING_BASIN.get(), new BlockEntityMechanicalMachine.Ticker<>());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(LIT);\n    }\n\n    @Override\n    public InteractionResult useWithoutItem(BlockState blockState, Level world, BlockPos blockPos, Player player, BlockHitResult rayTraceResult) {\n        if (FluidUtil.interactWithFluidHandler(player, player.getUsedItemHand(), world, blockPos, Direction.UP)\n                || FluidUtil.interactWithFluidHandler(player, player.getUsedItemHand(), world, blockPos, Direction.DOWN)) {\n            return InteractionResult.SUCCESS;\n        }\n        return super.useWithoutItem(blockState, world, blockPos, player, rayTraceResult);\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos blockPos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        if (!world.isClientSide()) {\n            IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityMechanicalDryingBasin.class)\n                    .ifPresent(tile -> {\n                        if (itemStack.has(RegistryEntries.DATACOMPONENT_FLUID_CONTENT_IN_OUT)) {\n                            Pair<SimpleFluidContent, SimpleFluidContent> tanks = itemStack.get(RegistryEntries.DATACOMPONENT_FLUID_CONTENT_IN_OUT);\n                            tile.getTankInput().setFluid(tanks.getLeft().copy());\n                            tile.getTankOutput().setFluid(tanks.getLeft().copy());\n                        }\n                    });\n        }\n        super.setPlacedBy(world, blockPos, state, placer, itemStack);\n    }\n\n    @Override\n    protected boolean isPickBlockPersistData() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMechanicalDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockMechanicalDryingBasin}.\n * @author rubensworks\n */\npublic class BlockMechanicalDryingBasinConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The energy capacity of a mechanical drying basin.\", minimalValue = 0)\n    public static int capacity = 100000;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The energy consumption rate.\", minimalValue = 0)\n    public static int consumptionRate = 80;\n\n    public BlockMechanicalDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_drying_basin\",\n                (eConfig, properties) -> new BlockMechanicalDryingBasin(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)\n                        .noOcclusion()\n                        .isRedstoneConductor((blockState, world, pos) -> false)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.BooleanProperty;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.neoforged.neoforge.transfer.fluid.FluidUtil;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalSqueezer;\nimport org.cyclops.integrateddynamics.core.block.BlockMechanicalMachine;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can expose variables.\n * @author rubensworks\n */\npublic class BlockMechanicalSqueezer extends BlockMechanicalMachine {\n\n    public static final MapCodec<BlockMechanicalSqueezer> CODEC = simpleCodec(BlockMechanicalSqueezer::new);\n\n    public static final String NBT_TANK = \"tank\";\n\n    public static final BooleanProperty LIT = BlockStateProperties.LIT;\n\n    public BlockMechanicalSqueezer(Properties properties) {\n        super(properties, BlockEntityMechanicalSqueezer::new);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(LIT, false));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_MECHANICAL_SQUEEZER.get(), new BlockEntityMechanicalSqueezer.Ticker());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(LIT);\n    }\n\n    @Override\n    public InteractionResult useWithoutItem(BlockState blockState, Level world, BlockPos blockPos, Player player, BlockHitResult rayTraceResult) {\n        if (FluidUtil.interactWithFluidHandler(player, player.getUsedItemHand(), world, blockPos, rayTraceResult.getDirection())) {\n            return InteractionResult.SUCCESS;\n        }\n        return super.useWithoutItem(blockState, world, blockPos, player, rayTraceResult);\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos blockPos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        if (!world.isClientSide()) {\n            IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityMechanicalSqueezer.class)\n                    .ifPresent(tile -> {\n                        if (itemStack.has(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_FLUID_CONTENT)) {\n                            tile.getTank().setFluidInTank(0, itemStack.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_FLUID_CONTENT).copy());\n                        }\n                    });\n        }\n        super.setPlacedBy(world, blockPos, state, placer, itemStack);\n    }\n\n    @Override\n    protected boolean isPickBlockPersistData() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMechanicalSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockMechanicalSqueezer}.\n * @author rubensworks\n */\npublic class BlockMechanicalSqueezerConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The energy capacity of a mechanical squeezer.\", minimalValue = 0)\n    public static int capacity = 100000;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The energy consumption rate.\", minimalValue = 0)\n    public static int consumptionRate = 80;\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"How many mB per tick can be auto-ejected.\", minimalValue = 0)\n    public static int autoEjectFluidRate = 500;\n\n    public BlockMechanicalSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_squeezer\",\n                (eConfig, properties) -> new BlockMechanicalSqueezer(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilDoorConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.DoorBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.properties.BlockSetType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Door.\n * @author josephcsible\n *\n */\npublic class BlockMenrilDoorConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilDoorConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_door\",\n                (eConfig, properties) -> new DoorBlock(BlockSetType.OAK, properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F, 3.0F)\n                        .sound(SoundType.WOOD)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilFenceConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.FenceBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Fence.\n * @author rubensworks\n *\n */\npublic class BlockMenrilFenceConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilFenceConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_fence\",\n                (eConfig, properties) -> new FenceBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F, 3.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilFenceGateConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.FenceGateBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.WoodType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Fence Gate\n * @author rubensworks\n *\n */\npublic class BlockMenrilFenceGateConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilFenceGateConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_fence_gate\",\n                (eConfig, properties) -> new FenceGateBlock(WoodType.OAK, properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F, 3.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilLeavesClientConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.client.color.block.BlockTintSources;\nimport net.neoforged.neoforge.client.event.RegisterColorHandlersEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.List;\n\n/**\n * Client config for the Menril Leaves block.\n * Registers a constant teal/cyan block tint source so that falling leaf particles\n * produced by {@link net.minecraft.world.level.block.TintedParticleLeavesBlock} are\n * coloured to match the Menril leaf texture (average colour #609198).\n *\n * @author rubensworks\n */\npublic class BlockMenrilLeavesClientConfig extends BlockClientConfig<IntegratedDynamics> {\n\n    // Bright mid-range teal of the Menril leaves texture (#76B4B8 = R118 G180 B184), stored as\n    // a signed Java ARGB int (0xFF76B4B8).  The raw average (#609198) was too dark because\n    // it was pulled down by shadow pixels; this value matches the dominant lit colour.\n    private static final int MENRIL_LEAF_COLOR = -8997704;\n\n    public BlockMenrilLeavesClientConfig(BlockConfigCommon<IntegratedDynamics> blockConfig) {\n        super(blockConfig);\n        blockConfig.getMod().getModEventBus().addListener(this::onRegisterColors);\n    }\n\n    public void onRegisterColors(RegisterColorHandlersEvent.BlockTintSources event) {\n        event.register(List.of(BlockTintSources.constant(MENRIL_LEAF_COLOR)), getBlockConfig().getInstance());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilLeavesConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.ComposterBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.TintedParticleLeavesBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Leaves.\n * @author rubensworks\n *\n */\npublic class BlockMenrilLeavesConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilLeavesConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_leaves\",\n                (eConfig, properties) -> new TintedParticleLeavesBlock(0.1F, properties\n                        .replaceable()\n                        .strength(0.2F)\n                        .randomTicks()\n                        .sound(SoundType.GRASS)\n                        .noOcclusion()) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n    @Override\n    public void onRegistryRegistered() {\n        super.onRegistryRegistered();\n        ComposterBlock.COMPOSTABLES.put(getItemInstance(), 0.3F);\n    }\n\n    @Override\n    public BlockClientConfig<IntegratedDynamics> constructBlockClientConfig() {\n        return new BlockMenrilLeavesClientConfig(this);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilLogConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.RotatedPillarBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Log.\n * @author rubensworks\n *\n */\npublic class BlockMenrilLogConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilLogConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_log\",\n                (eConfig, properties) -> new RotatedPillarBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilLogFilled.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.RotatedPillarBlock;\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.EnumProperty;\n\n/**\n * Menril wood block that is filled.\n * @author rubensworks\n */\npublic class BlockMenrilLogFilled extends RotatedPillarBlock {\n\n    public static final EnumProperty<Direction> SIDE = BlockStateProperties.HORIZONTAL_FACING;\n\n    public BlockMenrilLogFilled(Properties properties) {\n        super(properties);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(AXIS, Direction.Axis.Y)\n                .setValue(SIDE, Direction.NORTH));\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(AXIS, SIDE);\n    }\n\n    @Override\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(SIDE, context.getHorizontalDirection().getOpposite());\n    }\n\n    @Override\n    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n        return 5;\n    }\n\n    @Override\n    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n        return 20;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilLogFilledConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Wood.\n * @author rubensworks\n *\n */\npublic class BlockMenrilLogFilledConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"worldgeneration\", comment = \"The 1/x chance at which Menril Wood will be filled with Menril Resin when generated, the higher this value, the lower the chance.\", isCommandable = true, minimalValue = 0)\n    public static int filledMenrilLogChance = 10;\n\n    public BlockMenrilLogFilledConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_log_filled\",\n                (eConfig, properties) -> new BlockMenrilLogFilled(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F)\n                        .sound(SoundType.WOOD)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilLogStrippedConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.RotatedPillarBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.Property;\nimport net.minecraft.world.level.material.MapColor;\nimport net.neoforged.neoforge.common.ItemAbilities;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.event.level.BlockEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Stripped Menril Log.\n * @author rubensworks\n *\n */\npublic class BlockMenrilLogStrippedConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilLogStrippedConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_log_stripped\",\n                (eConfig, properties) -> new RotatedPillarBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F).sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n        NeoForge.EVENT_BUS.addListener(BlockMenrilLogStrippedConfig::toolActionEvent);\n    }\n\n    public static void toolActionEvent(BlockEvent.BlockToolModificationEvent event) {\n        if (event.getItemAbility() == ItemAbilities.AXE_STRIP && event.getState().getBlock() == RegistryEntries.BLOCK_MENRIL_LOG.get()) {\n            BlockState blockStateNew = RegistryEntries.BLOCK_MENRIL_LOG_STRIPPED.get().defaultBlockState();\n            for (Property property : event.getState().getProperties()) {\n                if(blockStateNew.hasProperty(property))\n                    blockStateNew = blockStateNew.setValue(property, event.getState().getValue(property));\n            }\n            event.setFinalState(blockStateNew);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilPlanksConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Wood Planks.\n * @author rubensworks\n *\n */\npublic class BlockMenrilPlanksConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilPlanksConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_planks\",\n                (eConfig, properties) -> new Block(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F, 3.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilPlanksStairsConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.StairBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Menril Wood Stairs.\n * @author rubensworks\n *\n */\npublic class BlockMenrilPlanksStairsConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilPlanksStairsConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_planks_stairs\",\n                (eConfig, properties) -> new StairBlock(RegistryEntries.BLOCK_MENRIL_PLANKS.get().defaultBlockState(), properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilSaplingConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.block.ComposterBlock;\nimport net.minecraft.world.level.block.SaplingBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.grower.TreeGrower;\nimport net.minecraft.world.level.levelgen.feature.ConfiguredFeature;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * Config for the Menril Sapling.\n * @author rubensworks\n *\n */\npublic class BlockMenrilSaplingConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public static final ResourceKey<ConfiguredFeature<?, ?>> MENTRIL_TREE = ResourceKey\n            .create(Registries.CONFIGURED_FEATURE, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"tree_menril\"));\n    public static final TreeGrower MENRIL_TREE_GROWER = new TreeGrower(\n            Reference.MOD_ID + \":menril_sapling\",\n            Optional.empty(), // Mega tree\n            Optional.of(MENTRIL_TREE),\n            Optional.empty() // Flowers\n    );\n\n    public BlockMenrilSaplingConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_sapling\",\n                (eConfig, properties) -> new SaplingBlock(MENRIL_TREE_GROWER, properties\n                        .noCollision()\n                        .randomTicks()\n                        .strength(0)\n                        .sound(SoundType.GRASS)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n    @Override\n    public void onRegistryRegistered() {\n        super.onRegistryRegistered();\n        ComposterBlock.COMPOSTABLES.put(getItemInstance(), 0.3F);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilSlabConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.SlabBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Slab.\n * @author rubensworks\n *\n */\npublic class BlockMenrilSlabConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilSlabConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_slab\",\n                (eConfig, properties) -> new SlabBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F, 3.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilTorchConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.particles.ParticleTypes;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.item.StandingAndWallBlockItem;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.TorchBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n\n/**\n * Config for the Menril Torch.\n * @author rubensworks\n *\n */\npublic class BlockMenrilTorchConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilTorchConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_torch\",\n                (eConfig, properties) -> new TorchBlock(ParticleTypes.FLAME, properties\n                        .noCollision()\n                        .strength(0)\n                        .lightLevel((blockState) -> 14)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public void animateTick(BlockState stateIn, Level worldIn, BlockPos pos, RandomSource rand) {\n                        // No particles\n                    }\n                },\n                (eConfig, block) -> new StandingAndWallBlockItem(block,\n                        RegistryEntries.BLOCK_MENRIL_TORCH_WALL.get(),\n                        Direction.DOWN, eConfig.createDefaultItemProperties())\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilTorchStoneConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.particles.ParticleTypes;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.item.StandingAndWallBlockItem;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.TorchBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n\n/**\n * Config for the Menril Stone Torch.\n * @author rubensworks\n *\n */\npublic class BlockMenrilTorchStoneConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilTorchStoneConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_torch_stone\",\n                (eConfig, properties) -> new TorchBlock(ParticleTypes.FLAME, properties\n                        .noCollision()\n                        .strength(0)\n                        .lightLevel((blockState) -> 14)\n                        .sound(SoundType.STONE)) {\n                    @Override\n                    public void animateTick(BlockState stateIn, Level worldIn, BlockPos pos, RandomSource rand) {\n                        // No particles\n                    }\n                },\n                (eConfig, block) -> new StandingAndWallBlockItem(block,\n                        RegistryEntries.BLOCK_MENRIL_TORCH_STONE_WALL.get(),\n                        Direction.DOWN, eConfig.createDefaultItemProperties())\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilTorchStoneWallConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.particles.ParticleTypes;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.WallTorchBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * Config for the Menril Stone Torch (wall).\n * @author rubensworks\n *\n */\npublic class BlockMenrilTorchStoneWallConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilTorchStoneWallConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_torch_stone_wall\",\n                (eConfig, properties) -> {\n                    return new WallTorchBlock(ParticleTypes.FLAME, properties\n                            .noCollision()\n                            .strength(0)\n                            .lightLevel((blockState) -> 14)\n                            .sound(SoundType.STONE)) {\n                        @Override\n                        public void animateTick(BlockState stateIn, Level worldIn, BlockPos pos, RandomSource rand) {\n                            // No particles\n                        }\n                    };\n                },\n                null\n        );\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        return Collections.emptyList();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilTorchWallConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.particles.ParticleTypes;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.WallTorchBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * Config for the Menril Torch (wall).\n * @author rubensworks\n *\n */\npublic class BlockMenrilTorchWallConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilTorchWallConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_torch_wall\",\n                (eConfig, properties) -> {\n                    return new WallTorchBlock(ParticleTypes.FLAME, properties\n                            .noCollision()\n                            .strength(0)\n                            .lightLevel((blockState) -> 14)\n                            .sound(SoundType.WOOD)) {\n                        @Override\n                        public void animateTick(BlockState stateIn, Level worldIn, BlockPos pos, RandomSource rand) {\n                            // No particles\n                        }\n                    };\n                },\n                null\n        );\n    }\n\n    @Override\n    public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilWoodConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.RotatedPillarBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.MapColor;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Stripped Menril Wood.\n * @author rubensworks\n *\n */\npublic class BlockMenrilWoodConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilWoodConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_wood\",\n                (eConfig, properties) -> new RotatedPillarBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockMenrilWoodStrippedConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.RotatedPillarBlock;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.Property;\nimport net.minecraft.world.level.material.MapColor;\nimport net.neoforged.neoforge.common.ItemAbilities;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.event.level.BlockEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the Stripped Menril Wood.\n * @author rubensworks\n *\n */\npublic class BlockMenrilWoodStrippedConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockMenrilWoodStrippedConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_wood_stripped\",\n                (eConfig, properties) -> new RotatedPillarBlock(properties\n                        .mapColor(MapColor.COLOR_CYAN)\n                        .strength(2.0F)\n                        .sound(SoundType.WOOD)) {\n                    @Override\n                    public int getFireSpreadSpeed(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 5;\n                    }\n\n                    @Override\n                    public int getFlammability(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {\n                        return 20;\n                    }\n                },\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n        NeoForge.EVENT_BUS.addListener(BlockMenrilWoodStrippedConfig::toolActionEvent);\n    }\n\n    public static void toolActionEvent(BlockEvent.BlockToolModificationEvent event) {\n        if (event.getItemAbility() == ItemAbilities.AXE_STRIP && event.getState().getBlock() == RegistryEntries.BLOCK_MENRIL_WOOD.get()) {\n            BlockState blockStateNew = RegistryEntries.BLOCK_MENRIL_WOOD_STRIPPED.get().defaultBlockState();\n            for (Property property : event.getState().getProperties()) {\n                if(blockStateNew.hasProperty(property))\n                    blockStateNew = blockStateNew.setValue(property, event.getState().getValue(property));\n            }\n            event.setFinalState(blockStateNew);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockProxy.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.LivingEntity;\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.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.core.block.BlockWithEntityGuiCabled;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can expose variables.\n * @author rubensworks\n */\npublic class BlockProxy extends BlockWithEntityGuiCabled {\n\n    public static final MapCodec<BlockProxy> CODEC = simpleCodec(BlockProxy::new);\n\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.HORIZONTAL_FACING;\n\n    public BlockProxy(Properties properties) {\n        super(properties, BlockEntityProxy::new);\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(FACING, Direction.NORTH));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_PROXY.get(), new BlockEntityProxy.Ticker<>());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING);\n    }\n\n    @Override\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection());\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos blockPos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        if (!world.isClientSide()) {\n            IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityProxy.class)\n                    .ifPresent(tile -> {\n                        if (itemStack.has(RegistryEntries.DATACOMPONENT_PROXY_ID)) {\n                            tile.setProxyId(itemStack.get(RegistryEntries.DATACOMPONENT_PROXY_ID));\n                        } else {\n                            tile.generateNewProxyId();\n                        }\n                        tile.setChanged();\n                    });\n        }\n        super.setPlacedBy(world, blockPos, state, placer, itemStack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockProxyConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.item.ItemBlockProxy;\n\n/**\n * Config for {@link BlockProxy}.\n * @author rubensworks\n */\npublic class BlockProxyConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockProxyConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"proxy\",\n                (eConfig, properties) -> new BlockProxy(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)),\n                (eConfig, block) -> new ItemBlockProxy(block, eConfig.createDefaultItemProperties())\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.util.Mth;\nimport net.minecraft.world.Containers;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.LivingEntity;\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.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\nimport net.minecraft.world.level.block.state.properties.IntegerProperty;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport org.cyclops.cyclopscore.block.BlockWithEntity;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntitySqueezer;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block for squeezing stuff.\n * @author rubensworks\n */\npublic class BlockSqueezer extends BlockWithEntity {\n\n    public static final MapCodec<BlockSqueezer> CODEC = simpleCodec(BlockSqueezer::new);\n    public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;\n    public static final IntegerProperty HEIGHT = IntegerProperty.create(\"height\", 1, 7); // 1 is heighest, 7 is lowest\n\n    private static final VoxelShape[] SHAPES_BLOCK = {\n            null,\n            box(0.0F, 0.0F, 0.0F, 16.0F, 16F, 16.0F),\n            box(0.0F, 0.0F, 0.0F, 16.0F, 14F, 16.0F),\n            box(0.0F, 0.0F, 0.0F, 16.0F, 12F, 16.0F),\n            box(0.0F, 0.0F, 0.0F, 16.0F, 10F, 16.0F),\n            box(0.0F, 0.0F, 0.0F, 16.0F, 8F, 16.0F),\n            box(0.0F, 0.0F, 0.0F, 16.0F, 6F, 16.0F),\n            box(0.0F, 0.0F, 0.0F, 16.0F, 4F, 16.0F),\n    };\n    private static final VoxelShape[] SHAPES_STICKS = {\n            box(0.0F, 0.0F, 0.0F, 2F, 16.0F, 2F),\n            box(14.0F, 0.0F, 0.0F, 16F, 16.0F, 2F),\n            box(0.0F, 0.0F, 14.0F, 2F, 16.0F, 16F),\n            box(14.0F, 0.0F, 14.0F, 16F, 16.0F, 16F),\n    };\n    private static final VoxelShape[] SHAPES = {\n            null,\n            Shapes.or(SHAPES_BLOCK[1], SHAPES_STICKS),\n            Shapes.or(SHAPES_BLOCK[2], SHAPES_STICKS),\n            Shapes.or(SHAPES_BLOCK[3], SHAPES_STICKS),\n            Shapes.or(SHAPES_BLOCK[4], SHAPES_STICKS),\n            Shapes.or(SHAPES_BLOCK[5], SHAPES_STICKS),\n            Shapes.or(SHAPES_BLOCK[6], SHAPES_STICKS),\n            Shapes.or(SHAPES_BLOCK[7], SHAPES_STICKS),\n    };\n\n    public BlockSqueezer(Properties properties) {\n        super(properties, BlockEntitySqueezer::new);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(AXIS, Direction.Axis.X)\n                .setValue(HEIGHT, 1));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_SQUEEZER.get(), new BlockEntitySqueezer.Ticker());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(AXIS, HEIGHT);\n    }\n\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(AXIS, context.getHorizontalDirection().getAxis());\n    }\n\n    @Override\n    public InteractionResult useWithoutItem(BlockState blockState, Level world, BlockPos blockPos, Player player, BlockHitResult blockRayTraceResult) {\n        if (world.isClientSide()) {\n            return InteractionResult.SUCCESS;\n        } else if(world.getBlockState(blockPos).getValue(BlockSqueezer.HEIGHT) == 1) {\n            return IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntitySqueezer.class)\n                    .map(tile -> {\n                        ItemStack itemStack = player.getInventory().getSelectedItem();\n                        ItemStack tileStack = tile.getInventory().getItem(0);\n\n                        if (itemStack.isEmpty() && !tileStack.isEmpty()) {\n                            player.getInventory().setItem(player.getInventory().getSelectedSlot(), tileStack);\n                            tile.getInventory().setItem(0, ItemStack.EMPTY);\n                            tile.sendUpdate();\n                            return InteractionResult.SUCCESS;\n                        } else if(player.getInventory().add(tileStack)){\n                            tile.getInventory().setItem(0, ItemStack.EMPTY);\n                            tile.sendUpdate();\n                            return InteractionResult.SUCCESS;\n                        } else if (!itemStack.isEmpty() && tile.getInventory().getItem(0).isEmpty()) {\n                            tile.getInventory().setItem(0, itemStack.split(1));\n                            if (itemStack.getCount() <= 0)\n                                player.getInventory().setItem(player.getInventory().getSelectedSlot(), ItemStack.EMPTY);\n                            tile.sendUpdate();\n                            return InteractionResult.SUCCESS;\n                        }\n                        return InteractionResult.PASS;\n                    })\n                    .orElse(InteractionResult.PASS);\n        }\n        return InteractionResult.PASS;\n    }\n\n    @Override\n    public void updateEntityMovementAfterFallOn(BlockGetter worldIn, Entity entityIn) {\n        double motionY = entityIn.getDeltaMovement().y;\n        super.updateEntityMovementAfterFallOn(worldIn, entityIn);\n        if(!entityIn.level().isClientSide() && motionY <= -0.37D && entityIn instanceof LivingEntity) {\n            // Same way of deriving blockPos as is done in Entity#moveEntity\n            int i = Mth.floor(entityIn.getX());\n            int j = Mth.floor(entityIn.getY() - 0.2D);\n            int k = Mth.floor(entityIn.getZ());\n            BlockPos blockPos = new BlockPos(i, j, k);\n            BlockState blockState = worldIn.getBlockState(blockPos);\n\n            // The faster the entity is falling, the more steps to advance by\n            int steps = 1 + Mth.floor((-motionY - 0.37D) * 5);\n\n            if (blockState.getBlock() == this) { // Just to be sure...\n                if((entityIn.getY() - blockPos.getY()) - getRelativeTopPositionTop(worldIn, blockPos, blockState) <= 0.1F) {\n                    int newHeight = Math.min(7, blockState.getValue(HEIGHT) + steps);\n                    entityIn.level().setBlockAndUpdate(blockPos, blockState.setValue(HEIGHT, newHeight));\n                    IModHelpers.get().getBlockEntityHelpers().get(worldIn, blockPos, BlockEntitySqueezer.class)\n                            .ifPresent(tile -> tile.setItemHeight(Math.max(newHeight, tile.getItemHeight())));\n                }\n            }\n        }\n    }\n\n    @Override\n    protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @org.jetbrains.annotations.Nullable Orientation orientation, boolean movedByPiston) {\n        super.neighborChanged(state, level, pos, neighborBlock, orientation, movedByPiston);\n        if (!level.isClientSide()) {\n            for (Direction enumfacing : Direction.values()) {\n                if (level.hasSignal(pos.relative(enumfacing), enumfacing)) {\n                    level.setBlockAndUpdate(pos, state.setValue(HEIGHT, 1));\n                    for(Entity entity : level.getEntitiesOfClass(Entity.class, new AABB(Vec3.atLowerCornerOf(pos), Vec3.atLowerCornerOf(pos.offset(1, 1, 1))))) {\n                        entity.getDeltaMovement().add(0, 0.25F, 0);\n                        entity.setDeltaMovement(0, 1, 0);\n                    }\n                    return;\n                }\n            }\n        }\n    }\n\n    public float getRelativeTopPositionTop(BlockGetter world, BlockPos blockPos, BlockState blockState) {\n        return (9 - blockState.getValue(HEIGHT)) * 0.125F;\n    }\n\n    @Override\n    public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        return SHAPES[blockState.getValue(HEIGHT)];\n    }\n\n    @Override\n    public VoxelShape getInteractionShape(BlockState blockState, BlockGetter world, BlockPos blockPos) {\n        return SHAPES_BLOCK[blockState.getValue(HEIGHT)];\n    }\n\n    @Override\n    public VoxelShape getCollisionShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        return SHAPES_BLOCK[blockState.getValue(HEIGHT)];\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public boolean hasAnalogOutputSignal(BlockState blockState) {\n        return true;\n    }\n\n    @Override\n    protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos, Direction direction) {\n        return (int) (((double) blockState.getValue(HEIGHT) - 1) / 6D * 15D);\n    }\n\n    @Override\n    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {\n        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);\n        Containers.updateNeighboursAfterDestroy(state, level, pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockSqueezer}.\n * @author rubensworks\n */\npublic class BlockSqueezerConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer\",\n                (eConfig, properties) -> new BlockSqueezer(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)\n                        .noOcclusion()\n                        .isRedstoneConductor((blockState, world, pos) -> false)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockVariablestore.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BaseEntityBlock;\nimport net.minecraft.world.level.block.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.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.block.BlockWithEntityGuiCabled;\n\nimport javax.annotation.Nullable;\n\n/**\n * A block that can hold defined variables so that they can be referred to elsewhere in the network.\n *\n * @author rubensworks\n */\npublic class BlockVariablestore extends BlockWithEntityGuiCabled {\n\n    public static final MapCodec<BlockVariablestore> CODEC = simpleCodec(BlockVariablestore::new);\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.HORIZONTAL_FACING;\n\n    public BlockVariablestore(Properties properties) {\n        super(properties, BlockEntityVariablestore::new);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(FACING, Direction.NORTH));\n    }\n\n    @Override\n    protected MapCodec<? extends BaseEntityBlock> codec() {\n        return CODEC;\n    }\n\n    @Override\n    @Nullable\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {\n        return level.isClientSide() ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_VARIABLE_STORE.get(), new BlockEntityVariablestore.Ticker());\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING);\n    }\n\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/BlockVariablestoreConfig.java",
    "content": "package org.cyclops.integrateddynamics.block;\n\nimport net.minecraft.world.level.block.SoundType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link BlockVariablestore}.\n * @author rubensworks\n */\npublic class BlockVariablestoreConfig extends BlockConfigCommon<IntegratedDynamics> {\n\n    public BlockVariablestoreConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"variablestore\",\n                (eConfig, properties) -> new BlockVariablestore(properties\n                        .strength(2.0F, 5.0F)\n                        .sound(SoundType.METAL)),\n                getDefaultItemConstructor(IntegratedDynamics._instance)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/shapes/CollisionContextBlockSupport.java",
    "content": "package org.cyclops.integrateddynamics.block.shapes;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.phys.shapes.EntityCollisionContext;\n\n/**\n * A collision context that is used to indicate that we're checking for block support,\n * e.g. when checking if a lever can be attached to a part.\n * @author rubensworks\n */\npublic class CollisionContextBlockSupport extends EntityCollisionContext {\n    public CollisionContextBlockSupport() {\n        super(false, false, -Double.MAX_VALUE, ItemStack.EMPTY, false, null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/shapes/VoxelShapeComponentsFactoryHandlerCableCenter.java",
    "content": "package org.cyclops.integrateddynamics.block.shapes;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\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.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.client.model.CableModel;\nimport org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * Shape handler for cable centers.\n * @author rubensworks\n */\npublic class VoxelShapeComponentsFactoryHandlerCableCenter implements VoxelShapeComponentsFactory.IHandler {\n\n    private static final VoxelShape BOUNDS = Shapes.create(new AABB(\n            CableModel.MIN, CableModel.MIN, CableModel.MIN,\n            CableModel.MAX, CableModel.MAX, CableModel.MAX));\n    private static final VoxelShapeComponentsFactoryHandlerCableCenter.Component COMPONENT = new Component();\n\n    @Override\n    public Collection<VoxelShapeComponents.IComponent> createComponents(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        if (world instanceof ILevelExtension level) {\n            if (CableHelpers.isNoFakeCable(level, blockPos, null, blockState)) {\n                return Collections.singletonList(COMPONENT);\n            }\n        }\n        return Collections.emptyList();\n    }\n\n    public static class Component implements VoxelShapeComponents.IComponent {\n\n        public String getStateId(BlockState blockState, BlockGetter world, BlockPos blockPos) {\n            return \"cent\";\n        }\n\n        @Override\n        public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n            return BOUNDS;\n        }\n\n        @Override\n        public ItemStack getCloneItemStack(Level world, BlockPos pos) {\n            return new ItemStack(RegistryEntries.BLOCK_CABLE.get());\n        }\n\n        @Override\n        public boolean destroy(Level world, BlockPos pos, Player player, boolean saveState) {\n            if (!world.isClientSide()) {\n                CableHelpers.removeCable(world, pos, player);\n                return true;\n            }\n            return false;\n        }\n\n        @Override\n        public InteractionResult onBlockActivated(BlockState state, Level world, BlockPos blockPos, Player player, InteractionHand hand, BlockRayTraceResultComponent hit) {\n            ItemStack heldItem = player.getItemInHand(hand);\n            InteractionResult actionResult = CableHelpers.onCableActivated(world, blockPos, state, player, heldItem, hit.getDirection(), null);\n            if(actionResult.consumesAction()) {\n                return actionResult;\n            }\n            return InteractionResult.PASS;\n        }\n\n        @Nullable\n        @Override\n        public Direction getRaytraceDirection() {\n            return null;\n        }\n\n        @Override\n        public boolean isRaytraceLastForFace() {\n            return false;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/shapes/VoxelShapeComponentsFactoryHandlerCableConnections.java",
    "content": "package org.cyclops.integrateddynamics.block.shapes;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\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.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.client.model.CableModel;\nimport org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\n\n/**\n * Shape handler for cable connections.\n * @author rubensworks\n */\npublic class VoxelShapeComponentsFactoryHandlerCableConnections implements VoxelShapeComponentsFactory.IHandler {\n\n    private final static EnumFacingMap<VoxelShape> BOUNDS = EnumFacingMap.forAllValues(\n            Shapes.create(new AABB(CableModel.MIN, 0, CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX)), // DOWN\n            Shapes.create(new AABB(CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX, 1, CableModel.MAX)), // UP\n            Shapes.create(new AABB(CableModel.MIN, CableModel.MIN, 0, CableModel.MAX, CableModel.MAX, CableModel.MIN)), // NORTH\n            Shapes.create(new AABB(CableModel.MIN, CableModel.MAX, CableModel.MAX, CableModel.MAX, CableModel.MIN, 1)), // SOUTH\n            Shapes.create(new AABB(0, CableModel.MIN, CableModel.MIN, CableModel.MIN, CableModel.MAX, CableModel.MAX)), // WEST\n            Shapes.create(new AABB(CableModel.MAX, CableModel.MIN, CableModel.MIN, 1, CableModel.MAX, CableModel.MAX)) // EAST\n    );\n\n    @Override\n    public Collection<VoxelShapeComponents.IComponent> createComponents(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        Collection<VoxelShapeComponents.IComponent> components = Lists.newArrayList();\n        if (world instanceof ILevelExtension level) {\n            if (CableHelpers.isNoFakeCable(level, blockPos, null, blockState)) {\n                for (Direction direction : Direction.values()) {\n                    IPartContainer partContainer = null;\n                    if (CableHelpers.isCableConnected((Level) world, blockPos, direction, blockState) ||\n                            (partContainer = PartHelpers.getPartContainer(level, blockPos, direction, blockState).orElse(null)) != null\n                                    && partContainer.hasPart(direction)) {\n                        components.add(new Component(direction, partContainer));\n                    }\n                }\n            }\n        }\n        return components;\n    }\n\n    public static class Component extends VoxelShapeComponentsFactoryHandlerCableCenter.Component {\n\n        private final Direction direction;\n        @Nullable\n        private final IPartContainer partContainer;\n\n        public Component(Direction direction, @Nullable IPartContainer partContainer) {\n            this.direction = direction;\n            this.partContainer = partContainer;\n        }\n\n        @Override\n        public String getStateId(BlockState blockState, BlockGetter world, BlockPos blockPos) {\n            return \"conn(\" + direction.ordinal() + \")\";\n        }\n\n        @Override\n        public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n            if (partContainer == null) { // equivalent to: CableHelpers.isCableConnected(world, blockPos, direction)\n                return BOUNDS.get(direction);\n            }\n            IPartType part = partContainer.getPart(direction);\n            if (part == null) {\n                // Can happen rarely on client desyncs\n                return BOUNDS.get(direction);\n            }\n            return part.getPartRenderPosition().getSidedCableBoundingBox(direction);\n        }\n\n        @Override\n        public InteractionResult onBlockActivated(BlockState state, Level world, BlockPos blockPos, Player player, InteractionHand hand, BlockRayTraceResultComponent hit) {\n            ItemStack heldItem = player.getItemInHand(hand);\n            InteractionResult actionResult = CableHelpers.onCableActivated(world, blockPos, state, player, heldItem, hit.getDirection(), direction);\n            if(actionResult.consumesAction()) {\n                return actionResult;\n            }\n            return InteractionResult.PASS;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/shapes/VoxelShapeComponentsFactoryHandlerFacade.java",
    "content": "package org.cyclops.integrateddynamics.block.shapes;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\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.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Optional;\n\n/**\n * Shape handler for facades.\n * @author rubensworks\n */\npublic class VoxelShapeComponentsFactoryHandlerFacade implements VoxelShapeComponentsFactory.IHandler {\n\n    private static final VoxelShape BOUNDS = Shapes.create(new AABB(\n            0, 0, 0,\n            1, 1, 1));\n    private static final VoxelShapeComponentsFactoryHandlerFacade.Component COMPONENT = new Component();\n\n    @Override\n    public Collection<VoxelShapeComponents.IComponent> createComponents(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        if (world instanceof ILevelExtension level) {\n            if (CableHelpers.hasFacade(level, blockPos, blockState)) {\n                return Collections.singletonList(COMPONENT);\n            }\n        }\n        return Collections.emptyList();\n    }\n\n    public static class Component implements VoxelShapeComponents.IComponent {\n\n        @Override\n        public String getStateId(BlockState blockState, BlockGetter world, BlockPos blockPos) {\n            String id = \"fac\";\n            Optional<BlockState> optionalFacade = CableHelpers.getFacade((Level) world, blockPos, blockState);\n            if (optionalFacade.isPresent()) {\n                id += \"(\" + optionalFacade.get().toString() + \")\";\n            }\n            return id;\n        }\n\n        @Override\n        public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n            return BOUNDS;\n        }\n\n        @Override\n        public ItemStack getCloneItemStack(Level world, BlockPos pos) {\n            ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_FACADE);\n            CableHelpers.getFacade(world, pos, world.getBlockState(pos))\n                    .ifPresent(facade -> RegistryEntries.ITEM_FACADE.get().writeFacadeBlock(itemStack, facade));\n            return itemStack;\n        }\n\n        @Override\n        public boolean destroy(Level world, BlockPos pos, Player player, boolean saveState) {\n            if(!world.isClientSide()) {\n                IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, Capabilities.Facadeable.BLOCK)\n                        .ifPresent(facadeable -> {\n                            BlockState blockState = facadeable.getFacade();\n                            ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_FACADE);\n                            RegistryEntries.ITEM_FACADE.get().writeFacadeBlock(itemStack, blockState);\n                            facadeable.setFacade(null);\n                            if (!player.isCreative()) {\n                                IModHelpers.get().getItemStackHelpers().spawnItemStackToPlayer(world, pos, itemStack, player);\n                            }\n                        });\n                return true;\n            }\n            return false;\n        }\n\n        @Override\n        public InteractionResult onBlockActivated(BlockState state, Level world, BlockPos blockPos, Player player, InteractionHand hand, BlockRayTraceResultComponent hit) {\n            ItemStack heldItem = player.getItemInHand(hand);\n            if(WrenchHelpers.isWrench(player, heldItem, world, blockPos, hit.getDirection()) && player.isSecondaryUseActive()) {\n                if (!world.isClientSide()) {\n                    destroy(world, blockPos, player, true);\n                    world.updateNeighborsAt(blockPos, state.getBlock());\n                }\n                return InteractionResult.SUCCESS;\n            }\n            return InteractionResult.PASS;\n        }\n\n        @Nullable\n        @Override\n        public Direction getRaytraceDirection() {\n            return null;\n        }\n\n        @Override\n        public boolean isRaytraceLastForFace() {\n            return true;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/block/shapes/VoxelShapeComponentsFactoryHandlerParts.java",
    "content": "package org.cyclops.integrateddynamics.block.shapes;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\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.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;\nimport org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\nimport org.cyclops.integrateddynamics.item.ItemBlockCable;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Optional;\n\n/**\n * Shape handler for parts.\n * @author rubensworks\n */\npublic class VoxelShapeComponentsFactoryHandlerParts implements VoxelShapeComponentsFactory.IHandler {\n\n    @Override\n    public Collection<VoxelShapeComponents.IComponent> createComponents(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        Collection<VoxelShapeComponents.IComponent> components = Lists.newArrayList();\n        if (world instanceof ILevelExtension level) {\n            for (Direction direction : Direction.values()) {\n                IPartContainer partContainer = PartHelpers.getPartContainer(level, blockPos, direction, blockState).orElse(null);\n                if (partContainer != null && partContainer.hasPart(direction)) {\n                    components.add(new Component(direction, partContainer));\n                }\n            }\n        }\n        return components;\n    }\n\n    public static class Component implements VoxelShapeComponents.IComponent {\n\n        private final Direction direction;\n        private final IPartContainer partContainer;\n\n        public Component(Direction direction, IPartContainer partContainer) {\n            this.direction = direction;\n            this.partContainer = partContainer;\n        }\n\n        public Optional<IPartType> getPart() {\n            return Optional.ofNullable(partContainer.getPart(direction));\n        }\n\n        @Override\n        public String getStateId(BlockState blockState, BlockGetter world, BlockPos blockPos) {\n            return getPart()\n                    .map(part -> \"part(\" + part.getPartRenderPosition().toCompactString() + \";dir=\" + this.direction.ordinal() + \")\")\n                    .orElse(\"part\");\n        }\n\n        @Override\n        public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n            return getPart()\n                    .map(part -> part.getPartRenderPosition().getBoundingBox(direction, selectionContext))\n                    .orElse(Shapes.empty());\n        }\n\n        @Override\n        public ItemStack getCloneItemStack(Level world, BlockPos pos) {\n            return getPart()\n                    .map(part -> part.getCloneItemStack(world, pos, partContainer.getPartState(direction)))\n                    .orElse(ItemStack.EMPTY);\n        }\n\n        @Override\n        public boolean destroy(Level world, BlockPos pos, Player player, boolean saveState) {\n            if(!world.isClientSide()) {\n                return PartHelpers.removePart(world, pos, direction, player, true, true, saveState);\n            }\n            return false;\n        }\n\n        @Override\n        public InteractionResult onBlockActivated(BlockState state, Level world, BlockPos blockPos, Player player, InteractionHand hand, BlockRayTraceResultComponent hit) {\n            ItemStack heldItem = player.getItemInHand(hand);\n            if(WrenchHelpers.isWrench(player, heldItem, world, blockPos, hit.getDirection()) && player.isSecondaryUseActive()) {\n                // Remove part from cable\n                if (!world.isClientSide()) {\n                    destroy(world, blockPos, player, true);\n                    ItemBlockCable.playBreakSound(world, blockPos, state);\n                }\n                return InteractionResult.SUCCESS;\n            } else if(CableHelpers.isNoFakeCable(world, blockPos, hit.getDirection())) {\n                // Delegate activated call to part\n                return getPart()\n                        .map(part -> part.onPartActivated(partContainer.getPartState(direction), blockPos, world,\n                                player, hand, heldItem, hit.withDirection(direction)))\n                        .orElse(InteractionResult.FAIL);\n            }\n            return InteractionResult.PASS;\n        }\n\n        @Nullable\n        @Override\n        public Direction getRaytraceDirection() {\n            return direction;\n        }\n\n        @Override\n        public boolean isRaytraceLastForFace() {\n            return false;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityCoalGenerator.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.item.ItemStackTemplate;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;\nimport org.cyclops.cyclopscore.datastructure.DataSlotSupplied;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.network.IEnergyNetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.block.BlockCoalGenerator;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityCableConnectableInventory;\nimport org.cyclops.integrateddynamics.core.helper.EnergyHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerCoalGenerator;\nimport org.cyclops.integrateddynamics.network.CoalGeneratorNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for the coal energy generator.\n * @author rubensworks\n */\npublic class BlockEntityCoalGenerator extends BlockEntityCableConnectableInventory implements MenuProvider {\n\n    public static final int INVENTORY_SIZE = 1;\n    public static final int MAX_PROGRESS = 13;\n    public static final int ENERGY_PER_TICK = 20;\n    public static final int SLOT_FUEL = 0;\n\n    @NBTPersist\n    private int currentlyBurningMax;\n    @NBTPersist\n    private int currentlyBurning;\n    private final EnergyHandler energyHandler;\n\n    public BlockEntityCoalGenerator(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_COAL_GENERATOR.get(), blockPos, blockState, BlockEntityCoalGenerator.INVENTORY_SIZE, 64);\n        this.energyHandler = new SimpleEnergyHandler(0);\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityCableConnectableInventory.CapabilityRegistrar<BlockEntityCoalGenerator> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityCoalGenerator>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, context) -> blockEntity.getNetworkElementProvider()\n            );\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK,\n                    (blockEntity, context) -> blockEntity.getEnergyHandler()\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new CoalGeneratorNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    public EnergyHandler getEnergyHandler() {\n        return energyHandler;\n    }\n\n    public Optional<IEnergyNetwork> getEnergyNetwork() {\n        return NetworkHelpers.getEnergyNetwork(getNetwork());\n    }\n\n    public void updateBlockState() {\n        boolean wasBurning = getLevel().getBlockState(getBlockPos()).getValue(BlockCoalGenerator.LIT);\n        boolean isBurning = isBurning();\n        if (isBurning != wasBurning) {\n            getLevel().setBlockAndUpdate(getBlockPos(),\n                    getLevel().getBlockState(getBlockPos()).setValue(BlockCoalGenerator.LIT, isBurning));\n        }\n    }\n\n    public int getProgress() {\n        float current = currentlyBurning;\n        float max = currentlyBurningMax;\n        if (max == 0) {\n            return -1;\n        }\n        return Math.round((current / max) * (float) MAX_PROGRESS);\n    }\n\n    public boolean isBurning() {\n        return currentlyBurning < currentlyBurningMax;\n    }\n\n    public boolean canAddEnergy(int energy) {\n        IEnergyNetwork network = getEnergyNetwork().orElse(null);\n        if(network != null && network.getChannel(IPositionedAddonsNetwork.DEFAULT_CHANNEL).insert((long) energy, true) == 0) {\n            return true;\n        }\n        return addEnergyFe(energy, true) == energy;\n    }\n\n    protected int addEnergy(int energy) {\n        IEnergyNetwork network = getEnergyNetwork().orElse(null);\n        int toFill = energy;\n        if(network != null) {\n            toFill = IModHelpers.get().getBaseHelpers().castSafe(network.getChannel(IPositionedAddonsNetwork.DEFAULT_CHANNEL).insert((long) toFill, false));\n        }\n        if(toFill > 0) {\n            toFill -= addEnergyFe(toFill, false);\n        }\n        return energy - toFill;\n    }\n\n    protected int addEnergyFe(int energy, boolean simulate) {\n        return EnergyHelpers.fillNeigbours(getLevel(), getBlockPos(), energy, simulate);\n    }\n\n    public static int getFuelTime(ItemStack itemStack) {\n        return itemStack.getBurnTime(RecipeType.SMELTING, ServerLifecycleHooks.getCurrentServer().fuelValues());\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.coal_generator\");\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerCoalGenerator(id, playerInventory, this.getInventory(), new DataSlotSupplied(this::getProgress));\n    }\n\n    public static class Ticker extends BlockEntityCableConnectableInventory.Ticker<BlockEntityCoalGenerator> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityCoalGenerator blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if((!blockEntity.getInventory().getItem(SLOT_FUEL).isEmpty() || blockEntity.isBurning()) && blockEntity.canAddEnergy(BlockEntityCoalGeneratorConfig.energyPerTick)) {\n                if (blockEntity.isBurning()) {\n                    if (blockEntity.currentlyBurning++ >= blockEntity.currentlyBurningMax) {\n                        blockEntity.currentlyBurning = 0;\n                        blockEntity.currentlyBurningMax = 0;\n                    }\n                    int toFill = BlockEntityCoalGeneratorConfig.energyPerTick;\n                    blockEntity.addEnergy(toFill);\n                    blockEntity.setChanged();\n                }\n                if (!blockEntity.isBurning()) {\n                    ItemStack fuel;\n                    if (getFuelTime(blockEntity.getInventory().getItem(SLOT_FUEL)) > 0\n                            && !(fuel = blockEntity.getInventory().removeItem(SLOT_FUEL, 1)).isEmpty()) {\n                        if(blockEntity.getInventory().getItem(SLOT_FUEL).isEmpty()) {\n                            ItemStackTemplate remainder = fuel.getItem().getCraftingRemainder(fuel);\n                            if (remainder != null) {\n                                blockEntity.getInventory().setItem(SLOT_FUEL, remainder.create());\n                            }\n                        }\n                        blockEntity.currentlyBurningMax = getFuelTime(fuel);\n                        blockEntity.currentlyBurning = 0;\n                        blockEntity.setChanged();\n                    }\n                    blockEntity.updateBlockState();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityCoalGeneratorConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.ModConfigLocation;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityCoalGenerator}.\n * @author rubensworks\n *\n */\npublic class BlockEntityCoalGeneratorConfig extends BlockEntityConfigCommon<BlockEntityCoalGenerator, IntegratedDynamics> {\n\n    @ConfigurablePropertyCommon(category = \"machine\", comment = \"The energy production rate (in RF/t) of the coal generator.\", minimalValue = 1, configLocation = ModConfigLocation.SERVER)\n    public static int energyPerTick = 20;\n\n    public BlockEntityCoalGeneratorConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"coal_generator\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityCoalGenerator::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_COAL_GENERATOR.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityCoalGenerator.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityDelay.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Queues;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.inventory.InventorySlotMasked;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.VariableAdapter;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IDelayVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.evaluate.DelayVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.item.DelayVariableFacade;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerDelay;\nimport org.cyclops.integrateddynamics.network.DelayNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for the variable delay.\n *\n * @author rubensworks\n */\npublic class BlockEntityDelay extends BlockEntityProxy implements MenuProvider {\n\n    public static final int INVENTORY_SIZE = 3;\n\n    protected Queue<IValue> values = null;\n    @NBTPersist\n    private int capacity = 5;\n    @NBTPersist\n    private int updateInterval = 1;\n    private ValueTypeList.ValueList list = ValueTypes.LIST.getDefault();\n    private final IVariable<?> variable;\n\n    private Player lastPlayer = null;\n    private EvaluationException lastError = null;\n\n    public int getCapacity() {\n        return capacity;\n    }\n\n    public int getUpdateInterval() {\n        return updateInterval;\n    }\n\n    public void setUpdateInterval(int updateInterval) {\n        this.updateInterval = updateInterval;\n    }\n\n    public void setLastPlayer(Player lastPlayer) {\n        this.lastPlayer = lastPlayer;\n    }\n\n    public BlockEntityDelay(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_DELAY.get(), blockPos, blockState, BlockEntityDelay.INVENTORY_SIZE);\n        this.variable = new VariableAdapter<ValueTypeList.ValueList>() {\n\n            @Override\n            public ValueTypeList getType() {\n                return ValueTypes.LIST;\n            }\n\n            @Override\n            public ValueTypeList.ValueList getValue() throws EvaluationException {\n                if (lastError != null) {\n                    throw lastError;\n                }\n                return list;\n            }\n        };\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityActiveVariableBase.CapabilityRegistrar<BlockEntityDelay> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityDelay>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, direction) -> {\n                        int slot = -1;\n                        if (direction != null) {\n                            switch (direction) {\n                                case DOWN -> slot = SLOT_WRITE_OUT;\n                                case UP -> slot = SLOT_WRITE_IN;\n                                case NORTH -> slot = SLOT_READ;\n                                case SOUTH -> slot = SLOT_READ;\n                                case WEST -> slot = SLOT_READ;\n                                case EAST -> slot = SLOT_READ;\n                            }\n                        } else {\n                            slot = SLOT_READ;\n                        }\n                        return VanillaContainerWrapper.of(new InventorySlotMasked(blockEntity.getInventory(), slot));\n                    }\n            );\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getNetworkElementProvider()\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new DelayNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    @Override\n    public ItemStack writeProxyInfo(boolean generateId, ItemStack itemStack, final int proxyId) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        return registry.writeVariableFacadeItem(generateId, itemStack, DelayVariableFacadeHandler.getInstance(), new IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IDelayVariableFacade>() {\n            @Override\n            public IDelayVariableFacade create(boolean generateId) {\n                return new DelayVariableFacade(generateId, proxyId);\n            }\n\n            @Override\n            public IDelayVariableFacade create(int id) {\n                return new DelayVariableFacade(id, proxyId);\n            }\n        }, getLevel(), lastPlayer, getBlockState());\n    }\n\n    @Override\n    public IVariable<?> getVariable(IPartNetwork network) {\n        return variable;\n    }\n\n    public IVariable<?> getVariableSuper(IPartNetwork network) {\n        return super.getVariable(network);\n    }\n\n    public void setCapacity(int capacity) {\n        this.capacity = Math.max(1, capacity);\n        this.values = Queues.newArrayBlockingQueue(this.capacity);\n    }\n\n    public Queue<IValue> getValues() {\n        if (values == null) {\n            values = Queues.newArrayBlockingQueue(this.capacity);\n        }\n        return values;\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        super.saveAdditional(output);\n        ValueOutput.ValueOutputList valueList = output.childrenList(\"values\");\n        for (IValue value : getValues()) {\n            ValueHelpers.serialize(valueList.addChild(), value);\n        }\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        if (this.capacity <= 0) this.capacity = 1;\n        values = Queues.newArrayBlockingQueue(this.capacity);\n\n        for (ValueInput valueInput : input.childrenList(\"values\").orElseThrow()) {\n            IValue value = ValueHelpers.deserialize(valueInput);\n            if (value != null) {\n                this.values.add(value);\n            }\n        }\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerDelay(id, playerInventory, this.getInventory(), Optional.of(this));\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.delay\");\n    }\n\n    public static class Ticker extends BlockEntityProxy.Ticker<BlockEntityDelay> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityDelay blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (!level.isClientSide() && blockEntity.updateInterval > 0 && level.getGameTime() % blockEntity.updateInterval == 0) {\n                // Remove oldest elements from the queue until we have room for a new one.\n                while (blockEntity.getValues().size() >= blockEntity.capacity) {\n                    blockEntity.getValues().poll();\n                }\n\n                IPartNetwork partNetwork = NetworkHelpers.getPartNetwork(blockEntity.getNetwork()).orElse(null);\n                if (partNetwork == null) {\n                    return;\n                }\n\n                // Add new value to the queue\n                IVariable<?> variable = blockEntity.getVariableSuper(partNetwork);\n                IValue value = null;\n                if (variable != null) {\n                    try {\n                        value = variable.getValue();\n                        blockEntity.lastError = null;\n                    } catch (EvaluationException e) {\n                        blockEntity.getEvaluator().addError(e.getErrorMessage());\n                        blockEntity.lastError = e;\n                    }\n                    if (value != null) {\n                        try {\n                            if (blockEntity.list.getRawValue().getLength() > 0 && blockEntity.list.getRawValue().getValueType() != value.getType()) {\n                                blockEntity.getValues().clear();\n                            }\n                        } catch (EvaluationException e) {}\n                        blockEntity.getValues().add(value);\n\n                        // Update variable with as value the materialized queue list\n                        blockEntity.list = ValueTypeList.ValueList.ofList(value.getType(), Lists.newArrayList(blockEntity.values));\n                    }\n                } else {\n                    blockEntity.getValues().clear();\n                    blockEntity.list = ValueTypes.LIST.getDefault();\n                }\n                blockEntity.variable.invalidate();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityDelayConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityDelay}.\n * @author rubensworks\n *\n */\npublic class BlockEntityDelayConfig extends BlockEntityConfigCommon<BlockEntityDelay, IntegratedDynamics> {\n\n    public BlockEntityDelayConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"delay\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityDelay::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_DELAY.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityDelay.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.core.particles.BlockParticleOption;\nimport net.minecraft.core.particles.ItemParticleOption;\nimport net.minecraft.core.particles.ParticleTypes;\nimport net.minecraft.world.Containers;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Blocks;\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.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.cyclopscore.capability.registrar.BlockEntityCapabilityRegistrar;\nimport org.cyclops.cyclopscore.datastructure.SingleCache;\nimport org.cyclops.cyclopscore.fluid.SingleUseTank;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.SimpleInventoryState;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.cyclopscore.recipe.type.InventoryFluid;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerDryingBasin;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeDryingBasin;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for drying stuff.\n * @author rubensworks\n */\npublic class BlockEntityDryingBasin extends CyclopsBlockEntity {\n\n    private static final int WOOD_IGNITION_TEMPERATURE = 573; // 300 degrees celcius\n\n    private final SimpleInventory inventory;\n    private final SingleUseTank tank;\n\n    @NBTPersist\n    private Float randomRotation = 0F;\n    @NBTPersist\n    private int progress = 0;\n    @NBTPersist\n    private int fire = 0;\n\n    private SingleCache<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeDryingBasin>>> recipeCache;\n\n    public BlockEntityDryingBasin(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_DRYING_BASIN.get(), blockPos, blockState);\n\n        // Create inventory and tank\n        this.inventory = new SimpleInventory(1, 1) {\n            @Override\n            public boolean canPlaceItem(int i, ItemStack itemstack) {\n                return getItem(0).isEmpty();\n            }\n\n            @Override\n            public void setItem(int slotId, ItemStack itemstack) {\n                super.setItem(slotId, itemstack);\n                BlockEntityDryingBasin.this.randomRotation = level.getRandom().nextFloat() * 360;\n                sendUpdate();\n            }\n        };\n        this.tank = new SingleUseTank(IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());\n\n        // Add dirty mark listeners to inventory and tank\n        this.inventory.addDirtyMarkListener(this::sendUpdate);\n        this.tank.addDirtyMarkListener(this.inventory::setChanged);\n\n        // Efficient cache to retrieve the current craftable recipe.\n        recipeCache = new SingleCache<>(new SingleCache.ICacheUpdater<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeDryingBasin>>>() {\n            @Override\n            public Optional<RecipeHolder<RecipeDryingBasin>> getNewValue(Pair<ItemStack, FluidStack> key) {\n                // First, try matching with both item and fluid inputs\n                IInventoryFluid recipeInput = new InventoryFluid(\n                        NonNullList.of(ItemStack.EMPTY, key.getLeft()),\n                        NonNullList.of(FluidStack.EMPTY, key.getRight()));\n                Optional<RecipeHolder<RecipeDryingBasin>> recipe = IModHelpers.get().getCraftingHelpers().findRecipe(getRegistry(), recipeInput, getLevel());\n                if (recipe.isPresent()) {\n                    return recipe;\n                }\n\n                // If both item and fluid are present but no combined recipe was found,\n                // try item-only, to handle the case where the machine has two separate\n                // types of inputs and should process one at a time.\n                if (!key.getLeft().isEmpty() && !key.getRight().isEmpty()) {\n                    recipeInput = new InventoryFluid(\n                            NonNullList.of(ItemStack.EMPTY, key.getLeft()),\n                            NonNullList.of(FluidStack.EMPTY, FluidStack.EMPTY));\n                    return IModHelpers.get().getCraftingHelpers().findRecipe(getRegistry(), recipeInput, getLevel());\n                }\n\n                return Optional.empty();\n            }\n\n            @Override\n            public boolean isKeyEqual(Pair<ItemStack, FluidStack> cacheKey, Pair<ItemStack, FluidStack> newKey) {\n                return cacheKey == null || newKey == null ||\n                        (ItemStack.matches(cacheKey.getLeft(), newKey.getLeft()) &&\n                                FluidStack.matches(cacheKey.getRight(), newKey.getRight()));\n            }\n        });\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityCapabilityRegistrar<BlockEntityDryingBasin> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityDryingBasin>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, direction) -> VanillaContainerWrapper.of(blockEntity.getInventory())\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.InventoryState.BLOCK,\n                    (blockEntity, direction) -> new SimpleInventoryState(blockEntity.getInventory())\n            );\n            add(\n                    Capabilities.Fluid.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getTank()\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK,\n                    (blockEntity, direction) -> new RecipeHandlerDryingBasin<>(blockEntity::getLevel, RegistryEntries.RECIPETYPE_DRYING_BASIN.get())\n            );\n        }\n    }\n\n    public int getProgress() {\n        return progress;\n    }\n\n    public void setProgress(int progress) {\n        this.progress = progress;\n    }\n\n    public int getFire() {\n        return fire;\n    }\n\n    public void setFire(int fire) {\n        this.fire = fire;\n    }\n\n    public SimpleInventory getInventory() {\n        return inventory;\n    }\n\n    public SingleUseTank getTank() {\n        return tank;\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        inventory.readFromNBT(input, \"inventory\");\n        tank.deserialize(input, \"tank\");\n        super.read(input);\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        inventory.writeToNBT(output, \"inventory\");\n        tank.serialize(output, \"tank\");\n        super.saveAdditional(output);\n    }\n\n    protected RecipeType<RecipeDryingBasin> getRegistry() {\n        return RegistryEntries.RECIPETYPE_DRYING_BASIN.get();\n    }\n\n    public Optional<RecipeHolder<RecipeDryingBasin>> getCurrentRecipe() {\n        return recipeCache.get(Pair.of(getInventory().getItem(0).copy(), IModHelpersNeoForge.get().getFluidHelpers().copy(getTank().getFluid())));\n    }\n\n    /**\n     * Get the random rotation for displaying the item.\n     * @return The random rotation.\n     */\n    public float getRandomRotation() {\n        return randomRotation;\n    }\n\n    @Override\n    public void preRemoveSideEffects(BlockPos pos, BlockState state) {\n        super.preRemoveSideEffects(pos, state);\n        Containers.dropContents(level, pos, this.getInventory());\n    }\n\n    public static class TickerServer extends BlockEntityTickerDelayed<BlockEntityDryingBasin> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityDryingBasin blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            Optional<RecipeHolder<RecipeDryingBasin>> currentRecipe = blockEntity.getCurrentRecipe();\n            if (!blockEntity.getTank().isEmpty() && blockEntity.getTank().getFluid().getFluid().getFluidType().getTemperature(blockEntity.getTank().getFluid()) >= WOOD_IGNITION_TEMPERATURE) {\n                blockEntity.setFire(blockEntity.getFire() + 1);\n                if (blockEntity.getFire() >= 100) {\n                    level.setBlockAndUpdate(pos, Blocks.FIRE.defaultBlockState());\n                } else if (level.isEmptyBlock(pos.relative(Direction.UP)) && level.getRandom().nextInt(10) == 0) {\n                    level.setBlockAndUpdate(pos.relative(Direction.UP), Blocks.FIRE.defaultBlockState());\n                }\n\n            } else if (currentRecipe.isPresent()) {\n                RecipeDryingBasin recipe = currentRecipe.get().value();\n                if (blockEntity.getProgress() >= recipe.getDuration()) {\n                    // Consume input fluid\n                    if (recipe.getInputFluid().isPresent()) {\n                        int amount = IModHelpersNeoForge.get().getFluidHelpers().getAmount(recipe.getInputFluid().get());\n                        try (var tx = Transaction.openRoot()) {\n                            blockEntity.getTank().extract(FluidResource.of(recipe.getInputFluid().get()), amount, tx);\n                            tx.commit();\n                        }\n                    }\n\n                    // Produce output item\n                    ItemStack output = recipe.getOutputItemFirst().orElse(ItemStack.EMPTY);\n                    if (!output.isEmpty()) {\n                        output = output.copy();\n                        blockEntity.getInventory().setItem(0, output);\n                    } else {\n                        blockEntity.getInventory().setItem(0, ItemStack.EMPTY);\n                    }\n\n                    // Produce output fluid\n                    if (recipe.getOutputFluid().isPresent()) {\n                        int inserted;\n                        try (var tx = Transaction.openRoot()) {\n                            FluidStack fluidStack = recipe.getOutputFluid().get();\n                            inserted = blockEntity.getTank().insert(FluidResource.of(fluidStack), fluidStack.getAmount(), tx);\n                            tx.commit();\n                        }\n                        if (inserted == 0) {\n                            IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, \"Encountered an invalid recipe: \" + currentRecipe.get().id());\n                        }\n                    }\n\n                    blockEntity.setProgress(0);\n                } else {\n                    blockEntity.setProgress(blockEntity.getProgress() + 1);\n                    blockEntity.setChanged();\n                }\n                blockEntity.setFire(0);\n            } else {\n                if ((blockEntity.getProgress() > 0) || (blockEntity.getFire() > 0)) {\n                    blockEntity.setProgress(0);\n                    blockEntity.setFire(0);\n                    blockEntity.setChanged();\n                }\n            }\n        }\n    }\n\n    public static class TickerClient implements BlockEntityTicker<BlockEntityDryingBasin> {\n        @Override\n        public void tick(Level level, BlockPos pos, BlockState blockState, BlockEntityDryingBasin blockEntity) {\n            if(blockEntity.getProgress() > 0 && level.getRandom().nextInt(5) == 0) {\n                if(!blockEntity.getTank().isEmpty()) {\n                    BlockState blockStateFluid = blockEntity.getTank().getFluid().getFluid().getFluidType().getBlockForFluidState(level, pos,\n                            blockEntity.getTank().getFluid().getFluid().defaultFluidState());\n                    if(blockStateFluid != null) {\n                        level.addParticle(new BlockParticleOption(ParticleTypes.FALLING_DUST, blockStateFluid),\n                                pos.getX() + Math.random() * 0.8D + 0.1D, pos.getY() + Math.random() * 0.1D + 0.9D,\n                                pos.getZ() + Math.random() * 0.8D + 0.1D, 0, 0.1D, 0);\n                    }\n                }\n                if(!blockEntity.getInventory().getItem(0).isEmpty()) {\n                    ItemStack itemStack = blockEntity.getInventory().getItem(0);\n                    level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, net.minecraft.world.item.ItemStackTemplate.fromNonEmptyStack(itemStack)),\n                            pos.getX() + Math.random() * 0.8D + 0.1D, pos.getY() + Math.random() * 0.1D + 0.9D,\n                            pos.getZ() + Math.random() * 0.8D + 0.1D, 0, 0.1D, 0);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityDryingBasin}.\n * @author rubensworks\n *\n */\npublic class BlockEntityDryingBasinConfig extends BlockEntityConfigCommon<BlockEntityDryingBasin, IntegratedDynamics> {\n\n    public BlockEntityDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"drying_basin\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityDryingBasin::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_DRYING_BASIN.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityDryingBasin.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n    @Override\n    public void onRegistered() {\n        super.onRegistered();\n        if (getMod().getModHelpers().getMinecraftHelpers().isClientSide()) {\n            new BlockEntityDryingBasinConfigClient().onRegistered(this);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityDryingBasinConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport org.cyclops.integrateddynamics.client.render.blockentity.RenderBlockEntityDryingBasin;\n\n/**\n * @author rubensworks\n */\npublic class BlockEntityDryingBasinConfigClient {\n\n    public void onRegistered(BlockEntityDryingBasinConfig config) {\n        config.getMod().getProxy().registerRenderer(config.getInstance(), RenderBlockEntityDryingBasin::new);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityEnergyBattery.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryBase;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryConfig;\nimport org.cyclops.integrateddynamics.capability.energystorage.SimpleEnergyHandlerCapacity;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityCableConnectable;\nimport org.cyclops.integrateddynamics.core.helper.EnergyHelpers;\nimport org.cyclops.integrateddynamics.network.EnergyBatteryNetworkElement;\n\nimport java.util.function.Supplier;\n\n/**\n * A part entity used to store variables.\n * Internally, this also acts as an expression cache\n * @author rubensworks\n */\npublic class BlockEntityEnergyBattery extends BlockEntityCableConnectable {\n\n    private SimpleEnergyHandlerCapacity energyHandler;\n\n    public BlockEntityEnergyBattery(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_ENERGY_BATTERY.get(), blockPos, blockState);\n        this.energyHandler = new SimpleEnergyHandlerCapacity(BlockEnergyBatteryConfig.capacity) {\n            @Override\n            protected void onEnergyChanged(int previousAmount) {\n                super.onEnergyChanged(previousAmount);\n                setChanged();\n                sendUpdate();\n            }\n        };\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityCableConnectable.CapabilityRegistrar<BlockEntityEnergyBattery> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityEnergyBattery>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, context) -> blockEntity.getNetworkElementProvider()\n            );\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK,\n                    (blockEntity, context) -> blockEntity.getEnergyHandler()\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new EnergyBatteryNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        energyHandler.deserialize(input);\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        super.saveAdditional(output);\n        energyHandler.serialize(output);\n    }\n\n    public SimpleEnergyHandlerCapacity getEnergyHandler() {\n        return energyHandler;\n    }\n\n    public boolean isCreative() {\n        Block block = getBlockState().getBlock();\n        return block instanceof BlockEnergyBatteryBase && ((BlockEnergyBatteryBase) block).isCreative();\n    }\n\n    public void setEnergyStored(int energy) {\n        this.energyHandler.set(energy);\n    }\n\n    public int getEnergyStored() {\n        return this.energyHandler.getAmountAsInt();\n    }\n\n    public int getMaxEnergyStored() {\n        return this.energyHandler.getCapacityAsInt();\n    }\n\n    @Override\n    public int getUpdateBackoffTicks() {\n        return 20;\n    }\n\n    public static int getEnergyPerTick(long capacity) {\n        return Math.max(IModHelpers.get().getBaseHelpers().castSafe(capacity) / BlockEnergyBatteryConfig.energyRateCapacityFraction, BlockEnergyBatteryConfig.minEnergyRate);\n    }\n\n    protected long getEnergyPerTick() {\n        return getEnergyPerTick(getMaxEnergyStored());\n    }\n\n    protected int addEnergy(int energy) {\n        int filled = addEnergyFe(energy, false);\n        try (var tx = Transaction.openRoot()) {\n            this.energyHandler.extract(filled, tx);\n            tx.commit();\n        }\n        return filled;\n    }\n\n    protected int addEnergyFe(int energy, boolean simulate) {\n        return EnergyHelpers.fillNeigbours(getLevel(), getBlockPos(), energy, simulate);\n    }\n\n    public static class Ticker extends BlockEntityCableConnectable.Ticker<BlockEntityEnergyBattery> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityEnergyBattery blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.getEnergyStored() > 0 && level.hasNeighborSignal(pos)) {\n                blockEntity.addEnergy(Math.min((int) blockEntity.getEnergyPerTick(), blockEntity.getEnergyStored()));\n            }\n        }\n\n        @Override\n        protected void onSendUpdate(Level level, BlockPos pos) {\n            BlockState blockState = level.getBlockState(pos);\n            level.sendBlockUpdated(pos, blockState, blockState,\n                    IModHelpers.get().getMinecraftHelpers().getBlockNotify() | IModHelpers.get().getMinecraftHelpers().getBlockNotifyClient() | IModHelpers.get().getMinecraftHelpers().getBlockNotifyNoRerender());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityEnergyBatteryConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityEnergyBattery}.\n * @author rubensworks\n *\n */\npublic class BlockEntityEnergyBatteryConfig extends BlockEntityConfigCommon<BlockEntityEnergyBattery, IntegratedDynamics> {\n\n    public BlockEntityEnergyBatteryConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"energy_battery\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityEnergyBattery::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_ENERGY_BATTERY.get(), RegistryEntries.BLOCK_ENERGY_BATTERY_CREATIVE.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityEnergyBattery.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n    @Override\n    public void onRegistered() {\n        super.onRegistered();\n        if (getMod().getModHelpers().getMinecraftHelpers().isClientSide()) {\n            new BlockEntityEnergyBatteryConfigClient().onRegistered(this);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityEnergyBatteryConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.client.renderer.texture.TextureAtlas;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.neoforge.client.event.TextureAtlasStitchedEvent;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.client.render.blockentity.RenderBlockEntityEnergyBattery;\n\n/**\n * @author rubensworks\n */\npublic class BlockEntityEnergyBatteryConfigClient {\n\n    public static TextureAtlasSprite ICON_OVERLAY;\n\n    public void onRegistered(BlockEntityEnergyBatteryConfig config) {\n        config.getMod().getProxy().registerRenderer(config.getInstance(), RenderBlockEntityEnergyBattery::new);\n        IntegratedDynamics._instance.getModEventBus().addListener(this::postTextureStitch);\n    }\n\n    public void postTextureStitch(TextureAtlasStitchedEvent event) {\n        if (event.getAtlas().location().equals(TextureAtlas.LOCATION_BLOCKS)) {\n            ICON_OVERLAY = event.getAtlas().getSprite(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"block/energy_battery_overlay\"));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMaterializer.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.inventory.InventorySlotMasked;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityCableConnectableInventory;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.item.ValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerMaterializer;\nimport org.cyclops.integrateddynamics.network.MaterializerNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for the variable materializer.\n * @author rubensworks\n */\npublic class BlockEntityMaterializer extends BlockEntityActiveVariableBase<MaterializerNetworkElement> implements MenuProvider {\n\n    public static final int INVENTORY_SIZE = 3;\n    public static final int SLOT_READ = 0;\n    public static final int SLOT_WRITE_IN = 1;\n    public static final int SLOT_WRITE_OUT = 2;\n\n    private Player lastPlayer = null;\n    private boolean writeVariable;\n\n    public void setLastPlayer(Player lastPlayer) {\n        this.lastPlayer = lastPlayer;\n    }\n\n    public BlockEntityMaterializer(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_MATERIALIZER.get(), blockPos, blockState, BlockEntityMaterializer.INVENTORY_SIZE);\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityCableConnectableInventory.CapabilityRegistrar<BlockEntityMaterializer> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityMaterializer>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, direction) -> {\n                        int slot = -1;\n                        if (direction != null) {\n                            switch (direction) {\n                                case DOWN -> slot = SLOT_WRITE_OUT;\n                                case UP -> slot = SLOT_WRITE_IN;\n                                case NORTH -> slot = SLOT_READ;\n                                case SOUTH -> slot = SLOT_READ;\n                                case WEST -> slot = SLOT_READ;\n                                case EAST -> slot = SLOT_READ;\n                            }\n                        } else {\n                            slot = SLOT_READ;\n                        }\n                        return VanillaContainerWrapper.of(new InventorySlotMasked(blockEntity.getInventory(), slot));\n                    }\n            );\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getNetworkElementProvider()\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new MaterializerNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    @Override\n    protected SimpleInventory createInventory(int inventorySize, int stackSize) {\n        return new SimpleInventory(inventorySize, stackSize) {\n            @Override\n            public boolean canPlaceItem(int slot, ItemStack itemStack) {\n                return slot != SLOT_WRITE_OUT && super.canPlaceItem(slot, itemStack);\n            }\n        };\n    }\n\n    @Override\n    public int getSlotRead() {\n        return SLOT_READ;\n    }\n\n    protected boolean canWrite() {\n        return NetworkHelpers.getPartNetwork(getNetwork())\n                .map(partNetwork -> getVariable(partNetwork) != null && getEvaluator().getErrors().isEmpty())\n                .orElse(false);\n    }\n\n    @Override\n    public void onDirty() {\n        super.onDirty();\n        if (!level.isClientSide()) {\n            this.writeVariable = true;\n        }\n    }\n\n    public ItemStack writeMaterialized(boolean generateId, ItemStack itemStack) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        IVariable variable = getVariable(NetworkHelpers.getPartNetworkChecked(getNetwork()));\n        try {\n            final IValue value = variable.getType().materialize(variable.getValue());\n            final IValueType valueType = value.getType();\n            return registry.writeVariableFacadeItem(generateId, itemStack, ValueTypes.REGISTRY, new IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IValueTypeVariableFacade>() {\n                @Override\n                public IValueTypeVariableFacade create(boolean generateId) {\n                    return new ValueTypeVariableFacade(generateId, valueType, value);\n                }\n\n                @Override\n                public IValueTypeVariableFacade create(int id) {\n                    return new ValueTypeVariableFacade(id, valueType, value);\n                }\n            }, getLevel(), lastPlayer, getBlockState());\n        } catch (EvaluationException e) {\n            getEvaluator().addError(Component.translatable(e.getMessage()));\n        }\n        return ItemStack.EMPTY;\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerMaterializer(id, playerInventory, this.getInventory(), Optional.of(this));\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.materializer\");\n    }\n\n    public static class Ticker extends BlockEntityCableConnectableInventory.Ticker<BlockEntityMaterializer> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityMaterializer blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.writeVariable && !blockEntity.getInventory().getItem(SLOT_WRITE_IN).isEmpty() && blockEntity.canWrite() && blockEntity.getInventory().getItem(SLOT_WRITE_OUT).isEmpty()) {\n                blockEntity.writeVariable = false;\n\n                // Write proxy reference\n                ItemStack outputStack = blockEntity.writeMaterialized(!blockEntity.getLevel().isClientSide(), blockEntity.getInventory().getItem(SLOT_WRITE_IN));\n                if(!outputStack.isEmpty()) {\n                    blockEntity.getInventory().setItem(SLOT_WRITE_OUT, outputStack);\n                    blockEntity.getInventory().removeItemNoUpdate(SLOT_WRITE_IN);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMaterializerConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityMaterializer}.\n * @author rubensworks\n *\n */\npublic class BlockEntityMaterializerConfig extends BlockEntityConfigCommon<BlockEntityMaterializer, IntegratedDynamics> {\n\n    public BlockEntityMaterializerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"materializer\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityMaterializer::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_MATERIALIZER.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityMaterializer.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.SingleCache;\nimport org.cyclops.cyclopscore.fluid.SingleUseTank;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.cyclopscore.recipe.type.InventoryFluid;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.block.BlockMechanicalDryingBasin;\nimport org.cyclops.integrateddynamics.block.BlockMechanicalDryingBasinConfig;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;\nimport org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerDryingBasin;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeMechanicalDryingBasin;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerMechanicalDryingBasin;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for the mechanical drying basin.\n * @author rubensworks\n */\npublic class BlockEntityMechanicalDryingBasin extends BlockEntityMechanicalMachine<Pair<ItemStack, FluidStack>, RecipeMechanicalDryingBasin>\n        implements MenuProvider {\n\n    public static final int INVENTORY_SIZE = 5;\n\n    private static final int SLOT_INPUT = 0;\n    private static final int[] SLOTS_OUTPUT = {1, 2, 3, 4};\n\n    private final SingleUseTank tankIn = new SingleUseTank(IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume() * 10);\n    private final SingleUseTank tankOut = new SingleUseTank(IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume() * 100);\n\n    public BlockEntityMechanicalDryingBasin(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_MECHANICAL_DRYING_BASIN.get(), blockPos, blockState, INVENTORY_SIZE);\n\n        // Add tank update listeners\n        tankIn.addDirtyMarkListener(this::onTankChanged);\n        tankOut.addDirtyMarkListener(this::onTankChanged);\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityMechanicalMachine.CapabilityRegistrar<BlockEntityMechanicalDryingBasin> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityMechanicalDryingBasin>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK,\n                    (blockEntity, direction) -> direction == Direction.DOWN ? blockEntity.getTankOutput() : blockEntity.getTankInput()\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK,\n                    (blockEntity, direction) -> new RecipeHandlerDryingBasin<>(blockEntity::getLevel, RegistryEntries.RECIPETYPE_MECHANICAL_DRYING_BASIN.get())\n            );\n        }\n    }\n\n    @Override\n    protected SingleCache.ICacheUpdater<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeMechanicalDryingBasin>>> createCacheUpdater() {\n        return new SingleCache.ICacheUpdater<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeMechanicalDryingBasin>>>() {\n            @Override\n            public Optional<RecipeHolder<RecipeMechanicalDryingBasin>> getNewValue(Pair<ItemStack, FluidStack> key) {\n                // First, try matching with both item and fluid inputs\n                IInventoryFluid recipeInput = new InventoryFluid(\n                        NonNullList.of(ItemStack.EMPTY, key.getLeft()),\n                        NonNullList.of(FluidStack.EMPTY, key.getRight()));\n                Optional<RecipeHolder<RecipeMechanicalDryingBasin>> recipe = IModHelpers.get().getCraftingHelpers().findRecipe(getRecipeRegistry(), recipeInput, getLevel());\n                if (recipe.isPresent()) {\n                    return recipe;\n                }\n\n                // If both item and fluid are present but no combined recipe was found,\n                // try item-only, then fluid-only, to handle the case where the machine has\n                // two separate types of inputs and should process one at a time.\n                if (!key.getLeft().isEmpty() && !key.getRight().isEmpty()) {\n                    recipeInput = new InventoryFluid(\n                            NonNullList.of(ItemStack.EMPTY, key.getLeft()),\n                            NonNullList.of(FluidStack.EMPTY, FluidStack.EMPTY));\n                    recipe = IModHelpers.get().getCraftingHelpers().findRecipe(getRecipeRegistry(), recipeInput, getLevel());\n                    if (recipe.isPresent()) {\n                        return recipe;\n                    }\n\n                    recipeInput = new InventoryFluid(\n                            NonNullList.of(ItemStack.EMPTY, ItemStack.EMPTY),\n                            NonNullList.of(FluidStack.EMPTY, key.getRight()));\n                    return IModHelpers.get().getCraftingHelpers().findRecipe(getRecipeRegistry(), recipeInput, getLevel());\n                }\n\n                return Optional.empty();\n            }\n\n            @Override\n            public boolean isKeyEqual(Pair<ItemStack, FluidStack> cacheKey, Pair<ItemStack, FluidStack> newKey) {\n                return cacheKey == null || newKey == null ||\n                        (ItemStack.matches(cacheKey.getLeft(), newKey.getLeft()) &&\n                                FluidStack.matches(cacheKey.getRight(), newKey.getRight()));\n            }\n        };\n    }\n\n    @Override\n    public int[] getInputSlots() {\n        return new int[]{SLOT_INPUT};\n    }\n\n    @Override\n    public int[] getOutputSlots() {\n        return SLOTS_OUTPUT;\n    }\n\n    @Override\n    public boolean wasWorking() {\n        return getLevel().getBlockState(getBlockPos()).getValue(BlockMechanicalDryingBasin.LIT);\n    }\n\n    @Override\n    public void setWorking(boolean working) {\n        getLevel().setBlockAndUpdate(getBlockPos(), getLevel().getBlockState(getBlockPos())\n                .setValue(BlockMechanicalDryingBasin.LIT, working));\n    }\n\n    public SingleUseTank getTankInput() {\n        return tankIn;\n    }\n\n    public SingleUseTank getTankOutput() {\n        return tankOut;\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        getTankInput().deserialize(input, \"tankIn\");\n        getTankOutput().deserialize(input, \"tankOut\");\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        getTankInput().serialize(output, \"tankIn\");\n        getTankOutput().serialize(output, \"tankOut\");\n        super.saveAdditional(output);\n    }\n\n    @Override\n    protected RecipeType<RecipeMechanicalDryingBasin> getRecipeRegistry() {\n        return RegistryEntries.RECIPETYPE_MECHANICAL_DRYING_BASIN.get();\n    }\n\n    @Override\n    protected Pair<ItemStack, FluidStack> getCurrentRecipeCacheKey() {\n        return Pair.of(getInventory().getItem(SLOT_INPUT).copy(), IModHelpersNeoForge.get().getFluidHelpers().copy(getTankInput().getFluid()));\n    }\n\n    @Override\n    public int getRecipeDuration(RecipeHolder<RecipeMechanicalDryingBasin> recipe) {\n        return recipe.value().getDuration();\n    }\n\n    @Override\n    protected boolean finalizeRecipe(RecipeMechanicalDryingBasin recipe, boolean simulate) {\n        // Output items\n        ItemStack outputStack = recipe.getOutputItemFirst().orElse(ItemStack.EMPTY).copy();\n        if (!outputStack.isEmpty()) {\n            if (!IModHelpers.get().getInventoryHelpers().addToInventory(getInventory(), SLOTS_OUTPUT, NonNullList.withSize(1, outputStack), simulate).isEmpty()) {\n                return false;\n            }\n        }\n\n        // Output fluid\n        Optional<FluidStack> outputFluid = recipe.getOutputFluid();\n        if (outputFluid.isPresent()) {\n            try (var tx = Transaction.openRoot()) {\n                int inserted = getTankOutput().insert(FluidResource.of(outputFluid.get()), outputFluid.get().getAmount(), tx);\n                if (!simulate) {\n                    tx.commit();\n                }\n                if (inserted != outputFluid.get().getAmount()) {\n                    return false;\n                }\n            }\n        }\n\n        // Only consume items if we are not simulating\n        if (!simulate) {\n            if (!recipe.getInputIngredient().isEmpty()) {\n                getInventory().removeItem(SLOT_INPUT, 1);\n            }\n        }\n\n        // Consume fluid\n        Optional<FluidStack> inputFluid = recipe.getInputFluid();\n        if (inputFluid.isPresent()) {\n            try (var tx = Transaction.openRoot()) {\n                int extracted = getTankInput().extract(FluidResource.of(inputFluid.get()), inputFluid.get().getAmount(), tx);\n                if (!simulate) {\n                    tx.commit();\n                }\n                if (extracted != inputFluid.get().getAmount()) {\n                    return false;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    @Override\n    public int getEnergyConsumptionRate() {\n        return BlockMechanicalDryingBasinConfig.consumptionRate;\n    }\n\n    @Override\n    public int getMaxEnergyStored() {\n        return BlockMechanicalDryingBasinConfig.capacity;\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerMechanicalDryingBasin(id, playerInventory, this.getInventory(), Optional.of(this));\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.mechanical_drying_basin\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMechanicalDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityMechanicalDryingBasin}.\n * @author rubensworks\n *\n */\npublic class BlockEntityMechanicalDryingBasinConfig extends BlockEntityConfigCommon<BlockEntityMechanicalDryingBasin, IntegratedDynamics> {\n\n    public BlockEntityMechanicalDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_drying_basin\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityMechanicalDryingBasin::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_MECHANICAL_DRYING_BASIN.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityMechanicalDryingBasin.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.base.Predicates;\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.ResourceHandlerUtil;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.datastructure.SingleCache;\nimport org.cyclops.cyclopscore.fluid.SingleUseTank;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.block.BlockMechanicalSqueezer;\nimport org.cyclops.integrateddynamics.block.BlockMechanicalSqueezerConfig;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;\nimport org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerSqueezer;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeMechanicalSqueezer;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeSqueezer;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerMechanicalSqueezer;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for the mechanical squeezer.\n * @author rubensworks\n */\npublic class BlockEntityMechanicalSqueezer extends BlockEntityMechanicalMachine<ItemStack, RecipeMechanicalSqueezer>\n        implements MenuProvider {\n\n    public static final int INVENTORY_SIZE = 5;\n\n    private static final int SLOT_INPUT = 0;\n    private static final int[] SLOTS_OUTPUT = {1, 2, 3, 4};\n    private static final int TANK_SIZE = IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume() * 100;\n\n    @NBTPersist\n    private boolean autoEjectFluids = false;\n\n    private final SingleUseTank tank = new SingleUseTank(TANK_SIZE);\n\n    public BlockEntityMechanicalSqueezer(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_MECHANICAL_SQUEEZER.get(), blockPos, blockState, INVENTORY_SIZE);\n\n        // Add tank update listeners\n        tank.addDirtyMarkListener(this::onTankChanged);\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityMechanicalMachine.CapabilityRegistrar<BlockEntityMechanicalSqueezer> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityMechanicalSqueezer>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getTank()\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK,\n                    (blockEntity, direction) -> new RecipeHandlerSqueezer<>(blockEntity::getLevel, RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER.get())\n            );\n        }\n    }\n\n    @Override\n    protected SingleCache.ICacheUpdater<ItemStack, Optional<RecipeHolder<RecipeMechanicalSqueezer>>> createCacheUpdater() {\n        return new SingleCache.ICacheUpdater<ItemStack, Optional<RecipeHolder<RecipeMechanicalSqueezer>>>() {\n            @Override\n            public Optional<RecipeHolder<RecipeMechanicalSqueezer>> getNewValue(ItemStack key) {\n                return IModHelpers.get().getCraftingHelpers().findRecipe(getRecipeRegistry(), CraftingInput.of(1, 1, Lists.newArrayList(key)), getLevel());\n            }\n\n            @Override\n            public boolean isKeyEqual(ItemStack cacheKey, ItemStack newKey) {\n                return ItemStack.matches(cacheKey, newKey);\n            }\n        };\n    }\n\n    @Override\n    public int[] getInputSlots() {\n        return new int[]{SLOT_INPUT};\n    }\n\n    @Override\n    public int[] getOutputSlots() {\n        return SLOTS_OUTPUT;\n    }\n\n    @Override\n    public boolean wasWorking() {\n        return getLevel().getBlockState(getBlockPos()).getValue(BlockMechanicalSqueezer.LIT);\n    }\n\n    @Override\n    public void setWorking(boolean working) {\n        getLevel().setBlockAndUpdate(getBlockPos(), getLevel().getBlockState(getBlockPos())\n                .setValue(BlockMechanicalSqueezer.LIT, working));\n    }\n\n    public SingleUseTank getTank() {\n        return tank;\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        getTank().deserialize(input, \"tank\");\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        getTank().serialize(output, \"tank\");\n        super.saveAdditional(output);\n    }\n\n    @Override\n    protected RecipeType<RecipeMechanicalSqueezer> getRecipeRegistry() {\n        return RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER.get();\n    }\n\n    @Override\n    protected ItemStack getCurrentRecipeCacheKey() {\n        return getInventory().getItem(SLOT_INPUT).copy();\n    }\n\n    @Override\n    public int getRecipeDuration(RecipeHolder<RecipeMechanicalSqueezer> recipe) {\n        return recipe.value().getDuration();\n    }\n\n    @Override\n    protected boolean finalizeRecipe(RecipeMechanicalSqueezer recipe, boolean simulate) {\n        // Output items\n        NonNullList<ItemStack> outputStacks = NonNullList.create();\n        for (RecipeSqueezer.IngredientChance itemStackChance : recipe.assemble(getInventory().getItem(SLOT_INPUT))) {\n            ItemStack outputStack = itemStackChance.getIngredientFirst().copy();\n            if (!outputStack.isEmpty() && (simulate || itemStackChance.getChance() == 1.0F\n                    || itemStackChance.getChance() >= getLevel().getRandom().nextFloat())) {\n                IModHelpers.get().getInventoryHelpers().addStackToList(outputStacks, outputStack);\n            }\n        }\n        if (!IModHelpers.get().getInventoryHelpers().addToInventory(getInventory(), SLOTS_OUTPUT, outputStacks, simulate).isEmpty()) {\n            return false;\n        }\n\n        // Output fluid\n        Optional<FluidStack> outputFluid = recipe.getOutputFluid();\n        if (outputFluid.isPresent()) {\n            try (var tx = Transaction.openRoot()) {\n                int inserted = getTank().insert(FluidResource.of(outputFluid.get()), outputFluid.get().getAmount(), tx);\n                if (!simulate) {\n                    tx.commit();\n                }\n                if (inserted != outputFluid.get().getAmount()) {\n                    return false;\n                }\n            }\n        }\n\n        // Only consume items if we are not simulating\n        if (!simulate) {\n            getInventory().removeItem(SLOT_INPUT, 1);\n        }\n\n        return true;\n    }\n\n    @Override\n    public int getEnergyConsumptionRate() {\n        return BlockMechanicalSqueezerConfig.consumptionRate;\n    }\n\n    @Override\n    public int getMaxEnergyStored() {\n        return BlockMechanicalSqueezerConfig.capacity;\n    }\n\n    public boolean isAutoEjectFluids() {\n        return autoEjectFluids;\n    }\n\n    public void setAutoEjectFluids(boolean autoEjectFluids) {\n        this.autoEjectFluids = autoEjectFluids;\n        sendUpdate();\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerMechanicalSqueezer(id, playerInventory, this.getInventory(), Optional.of(this));\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.mechanical_squeezer\");\n    }\n\n    public static class Ticker extends BlockEntityMechanicalMachine.Ticker<ItemStack, RecipeMechanicalSqueezer, BlockEntityMechanicalSqueezer> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityMechanicalSqueezer blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            // Auto-eject fluid\n            if (blockEntity.isAutoEjectFluids() && !blockEntity.getTank().isEmpty()) {\n                for (Direction side : Direction.values()) {\n                    ResourceHandler<FluidResource> handler = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(level, pos.relative(side),\n                            side.getOpposite(), net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK).orElse(null);\n                    if(handler != null) {\n                        FluidStack fluidStack = blockEntity.getTank().getFluid().copy();\n                        fluidStack.setAmount(Math.min(BlockMechanicalSqueezerConfig.autoEjectFluidRate, fluidStack.getAmount()));\n                        try (var tx = Transaction.openRoot()) {\n                            int moved = ResourceHandlerUtil.move(blockEntity.getTank(), handler, Predicates.alwaysTrue(), BlockMechanicalSqueezerConfig.autoEjectFluidRate, tx);\n                            tx.commit();\n                            if (moved > 0) {\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMechanicalSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityMechanicalSqueezer}.\n * @author rubensworks\n *\n */\npublic class BlockEntityMechanicalSqueezerConfig extends BlockEntityConfigCommon<BlockEntityMechanicalSqueezer, IntegratedDynamics> {\n\n    public BlockEntityMechanicalSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_squeezer\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityMechanicalSqueezer::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_MECHANICAL_SQUEEZER.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityMechanicalSqueezer.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMultipartTickingConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport org.cyclops.integrateddynamics.client.render.blockentity.RenderCable;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTickingConfig;\n\n/**\n * @author rubensworks\n */\npublic class BlockEntityMultipartTickingConfigClient {\n    public void onRegistered(BlockEntityMultipartTickingConfig config) {\n        config.getMod().getProxy().registerRenderer(config.getInstance(), RenderCable::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityProxy.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.inventory.InventorySlotMasked;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IProxyVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityCableConnectableInventory;\nimport org.cyclops.integrateddynamics.core.evaluate.InventoryVariableEvaluator;\nimport org.cyclops.integrateddynamics.core.evaluate.ProxyVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.item.ProxyVariableFacade;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerProxy;\nimport org.cyclops.integrateddynamics.network.ProxyNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for the variable proxy.\n * @author rubensworks\n */\npublic class BlockEntityProxy extends BlockEntityActiveVariableBase<ProxyNetworkElement> implements MenuProvider {\n\n    public static final int INVENTORY_SIZE = 3;\n    public static final int SLOT_READ = 0;\n    public static final int SLOT_WRITE_IN = 1;\n    public static final int SLOT_WRITE_OUT = 2;\n\n    public static final String GLOBALCOUNTER_KEY = \"proxy\";\n\n    @NBTPersist\n    private int proxyId = -1;\n\n    private Player lastPlayer = null;\n    private boolean writeVariable;\n\n    public int getProxyId() {\n        return proxyId;\n    }\n\n    public void setProxyId(int proxyId) {\n        this.proxyId = proxyId;\n    }\n\n    public void setLastPlayer(Player lastPlayer) {\n        this.lastPlayer = lastPlayer;\n    }\n\n    public BlockEntityProxy(BlockPos blockPos, BlockState blockState) {\n        this(RegistryEntries.BLOCK_ENTITY_PROXY.get(), blockPos, blockState, BlockEntityProxy.INVENTORY_SIZE);\n    }\n\n    public BlockEntityProxy(BlockEntityType<?> type, BlockPos blockPos, BlockState blockState, int inventorySize) {\n        super(type, blockPos, blockState, inventorySize);\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityActiveVariableBase.CapabilityRegistrar<BlockEntityProxy> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityProxy>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, direction) -> {\n                        int slot = -1;\n                        if (direction != null) {\n                            switch (direction) {\n                                case DOWN -> slot = SLOT_WRITE_OUT;\n                                case UP -> slot = SLOT_WRITE_IN;\n                                case NORTH -> slot = SLOT_READ;\n                                case SOUTH -> slot = SLOT_READ;\n                                case WEST -> slot = SLOT_READ;\n                                case EAST -> slot = SLOT_READ;\n                            }\n                        } else {\n                            slot = SLOT_READ;\n                        }\n                        return VanillaContainerWrapper.of(new InventorySlotMasked(blockEntity.getInventory(), slot));\n                    }\n            );\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getNetworkElementProvider()\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new ProxyNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    public boolean isWriteVariable() {\n        return writeVariable;\n    }\n\n    @Override\n    protected SimpleInventory createInventory(int inventorySize, int stackSize) {\n        return new SimpleInventory(inventorySize, stackSize) {\n            @Override\n            public boolean canPlaceItem(int slot, ItemStack itemStack) {\n                return slot != SLOT_WRITE_OUT && super.canPlaceItem(slot, itemStack);\n            }\n        };\n    }\n\n    @Override\n    protected InventoryVariableEvaluator<IValue> createEvaluator() {\n        return new InventoryVariableEvaluator<IValue>(this.getInventory(), getSlotRead(), () -> ValueDeseralizationContext.of(getLevel()), ValueTypes.CATEGORY_ANY) {\n            @Override\n            protected void preValidate() {\n                super.preValidate();\n                // Hard check to make sure the variable is not directly referring to this proxy.\n                if(getVariableFacade() instanceof IProxyVariableFacade) {\n                    if(((IProxyVariableFacade) getVariableFacade()).getProxyId() == getProxyId()) {\n                        addError(Component.translatable(L10NValues.VARIABLE_ERROR_RECURSION, getVariableFacade().getId()));\n                    }\n                }\n            }\n        };\n    }\n\n    /**\n     * This will generate a new proxy id.\n     * Be careful when calling this!\n     */\n    public void generateNewProxyId() {\n        this.proxyId = IntegratedDynamics.globalCounters.get().getNext(GLOBALCOUNTER_KEY);\n        setChanged();\n    }\n\n    @Override\n    public int getSlotRead() {\n        return SLOT_READ;\n    }\n\n    protected int getSlotWriteIn() {\n        return SLOT_WRITE_IN;\n    }\n\n    protected int getSlotWriteOut() {\n        return SLOT_WRITE_OUT;\n    }\n\n    @Override\n    public void onDirty() {\n        super.onDirty();\n        if (!level.isClientSide()) {\n            this.writeVariable = true;\n        }\n    }\n\n    public ItemStack writeProxyInfo(boolean generateId, ItemStack itemStack, final int proxyId) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        return registry.writeVariableFacadeItem(generateId, itemStack, ProxyVariableFacadeHandler.getInstance(), new IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IProxyVariableFacade>() {\n            @Override\n            public IProxyVariableFacade create(boolean generateId) {\n                return new ProxyVariableFacade(generateId, proxyId);\n            }\n\n            @Override\n            public IProxyVariableFacade create(int id) {\n                return new ProxyVariableFacade(id, proxyId);\n            }\n        }, getLevel(), lastPlayer, getBlockState());\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerProxy(id, playerInventory, this.getInventory(), Optional.of(this));\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.proxy\");\n    }\n\n    public static class Ticker<T extends BlockEntityProxy> extends BlockEntityCableConnectableInventory.Ticker<T> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, T blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            // If the block was placed by a non-player, this is needed.\n            if (blockEntity.getProxyId() < 0) {\n                blockEntity.generateNewProxyId();\n                NetworkHelpers.initNetwork(level, pos, null);\n            }\n\n            if (blockEntity.isWriteVariable()) {\n                if (!blockEntity.getInventory().getItem(blockEntity.getSlotWriteIn()).isEmpty() && blockEntity.getInventory().getItem(blockEntity.getSlotWriteOut()).isEmpty()) {\n                    // Write proxy reference\n                    ItemStack outputStack = blockEntity.writeProxyInfo(!blockEntity.getLevel().isClientSide(), blockEntity.getInventory().removeItemNoUpdate(blockEntity.getSlotWriteIn()), blockEntity.getProxyId());\n                    blockEntity.getInventory().setItem(blockEntity.getSlotWriteOut(), outputStack);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityProxyConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityProxy}.\n * @author rubensworks\n *\n */\npublic class BlockEntityProxyConfig extends BlockEntityConfigCommon<BlockEntityProxy, IntegratedDynamics> {\n\n    public BlockEntityProxyConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"proxy\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityProxy::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_PROXY.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityProxy.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntitySqueezer.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.Containers;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.ResourceHandlerUtil;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.cyclopscore.capability.registrar.BlockEntityCapabilityRegistrar;\nimport org.cyclops.cyclopscore.datastructure.SingleCache;\nimport org.cyclops.cyclopscore.fluid.SingleUseTank;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.SimpleInventoryState;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.block.BlockSqueezer;\nimport org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerSqueezer;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeSqueezer;\n\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A part entity for squeezing stuff.\n * @author rubensworks\n */\npublic class BlockEntitySqueezer extends CyclopsBlockEntity {\n\n    private final SimpleInventory inventory;\n    private final SingleUseTank tank;\n\n    @NBTPersist\n    private int itemHeight = 1;\n\n    private SingleCache<ItemStack, Optional<RecipeHolder<RecipeSqueezer>>> recipeCache;\n\n    public int getItemHeight() {\n        return itemHeight;\n    }\n\n    public BlockEntitySqueezer(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_SQUEEZER.get(), blockPos, blockState);\n\n        // Create inventory and tank\n        this.inventory = new SimpleInventory(1, 1) {\n            @Override\n            public boolean canPlaceItem(int slot, ItemStack itemStack) {\n                return getLevel().getBlockState(getBlockPos()).getValue(BlockSqueezer.HEIGHT) == 1\n                        && getItem(0).isEmpty() && super.canPlaceItem(slot, itemStack);\n            }\n\n            @Override\n            public void setItem(int slotId, ItemStack itemstack) {\n                super.setItem(slotId, itemstack);\n                itemHeight = 1;\n                sendUpdate();\n            }\n        };\n        this.tank = new SingleUseTank(IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());\n\n        // Add dirty mark listeners to inventory and tank\n        this.inventory.addDirtyMarkListener(this::sendUpdate);\n        this.tank.addDirtyMarkListener(this.inventory::setChanged);\n\n        // Efficient cache to retrieve the current craftable recipe.\n        recipeCache = new SingleCache<>(\n                new SingleCache.ICacheUpdater<ItemStack, Optional<RecipeHolder<RecipeSqueezer>>>() {\n                    @Override\n                    public Optional<RecipeHolder<RecipeSqueezer>> getNewValue(ItemStack key) {\n                        return IModHelpers.get().getCraftingHelpers().findRecipe(getRegistry(), CraftingInput.of(1, 1, Lists.newArrayList(key)), getLevel());\n                    }\n\n                    @Override\n                    public boolean isKeyEqual(ItemStack cacheKey, ItemStack newKey) {\n                        return ItemStack.matches(cacheKey, newKey);\n                    }\n                });\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityCapabilityRegistrar<BlockEntitySqueezer> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntitySqueezer>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, direction) -> VanillaContainerWrapper.of(blockEntity.getInventory())\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.InventoryState.BLOCK,\n                    (blockEntity, direction) -> new SimpleInventoryState(blockEntity.getInventory())\n            );\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getTank()\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK,\n                    (blockEntity, direction) -> new RecipeHandlerSqueezer<>(blockEntity::getLevel, RegistryEntries.RECIPETYPE_SQUEEZER.get())\n            );\n        }\n    }\n\n    public SimpleInventory getInventory() {\n        return inventory;\n    }\n\n    public SingleUseTank getTank() {\n        return tank;\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        inventory.readFromNBT(input, \"inventory\");\n        tank.deserialize(input, \"tank\");\n        super.read(input);\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        inventory.writeToNBT(output, \"inventory\");\n        tank.serialize(output, \"tank\");\n        super.saveAdditional(output);\n    }\n\n    protected RecipeType<RecipeSqueezer> getRegistry() {\n        return RegistryEntries.RECIPETYPE_SQUEEZER.get();\n    }\n\n    public Optional<RecipeHolder<RecipeSqueezer>> getCurrentRecipe() {\n        return recipeCache.get(getInventory().getItem(0).copy());\n    }\n\n    public void setItemHeight(int itemHeight) {\n        this.itemHeight = itemHeight;\n        sendUpdate();\n        getInventory().setChanged();\n    }\n\n    @Override\n    public void preRemoveSideEffects(BlockPos pos, BlockState state) {\n        super.preRemoveSideEffects(pos, state);\n        Containers.dropContents(level, pos, this.getInventory());\n    }\n\n    public static class Ticker extends BlockEntityTickerDelayed<BlockEntitySqueezer> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntitySqueezer blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if(!blockEntity.getTank().isEmpty()) {\n                Direction.Axis axis = blockState.getValue(BlockSqueezer.AXIS);\n                Arrays.stream(Direction.AxisDirection.values())\n                        .map(axisDirection -> Direction.get(axisDirection, axis))\n                        .forEach(side -> {\n                            if (!blockEntity.getTank().isEmpty()) {\n                                IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(level, pos.relative(side), side.getOpposite(), net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK)\n                                        .ifPresent(handler -> {\n                                            FluidStack fluidStack = new FluidStack(blockEntity.getTank().getFluid().getFluid(),\n                                                    Math.min(100, blockEntity.getTank().getFluidAmount()));\n                                            ResourceHandlerUtil.move(blockEntity.getTank(), handler, f -> f.matches(fluidStack), fluidStack.getAmount(), null);\n                                        });\n                            }\n                        });\n            } else {\n                if (blockEntity.itemHeight == 7) {\n                    Optional<RecipeHolder<RecipeSqueezer>> recipeOptional = blockEntity.getCurrentRecipe();\n                    if (recipeOptional.isPresent()) {\n                        RecipeSqueezer recipe = recipeOptional.get().value();\n                        ItemStack oldItem = blockEntity.getInventory().getItem(0);\n                        blockEntity.getInventory().setItem(0, ItemStack.EMPTY);\n                        for (RecipeSqueezer.IngredientChance itemStackChance : recipe.assemble(oldItem)) {\n                            if (itemStackChance.getChance() == 1.0F || itemStackChance.getChance() >= level.getRandom().nextFloat()) {\n                                ItemStack resultStack = itemStackChance.getIngredientFirst().copy();\n                                for (Direction side : Direction.values()) {\n                                    if (!resultStack.isEmpty() && side != Direction.UP) {\n                                        ResourceHandler<ItemResource> itemHandler = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(level, pos.relative(side), side.getOpposite(), Capabilities.Item.BLOCK).orElse(null);\n                                        if (itemHandler != null) {\n                                            try (var tx = Transaction.openRoot()) {\n                                                int inserted = itemHandler.insert(ItemResource.of(resultStack), resultStack.getCount(), tx);\n                                                tx.commit();\n                                                resultStack.shrink(inserted);\n                                            }\n                                        }\n                                    }\n                                }\n                                if (!resultStack.isEmpty()) {\n                                    IModHelpers.get().getItemStackHelpers().spawnItemStack(level, pos, resultStack);\n                                }\n                            }\n                        }\n                        if (recipe.getOutputFluid().isPresent()) {\n                            try (var tx = Transaction.openRoot()) {\n                                FluidStack fluidStack = recipe.getOutputFluid().get();\n                                blockEntity.getTank().insert(FluidResource.of(fluidStack), fluidStack.getAmount(), tx);\n                                tx.commit();\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntitySqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntitySqueezer}.\n * @author rubensworks\n *\n */\npublic class BlockEntitySqueezerConfig extends BlockEntityConfigCommon<BlockEntitySqueezer, IntegratedDynamics> {\n\n    public BlockEntitySqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer\",\n                (eConfig) -> new BlockEntityType<>(BlockEntitySqueezer::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_SQUEEZER.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntitySqueezer.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n    @Override\n    public void onRegistered() {\n        super.onRegistered();\n        if (getMod().getModHelpers().getMinecraftHelpers().isClientSide()) {\n            new BlockEntitySqueezerConfigClient().onRegistered(this);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntitySqueezerConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport org.cyclops.integrateddynamics.client.render.blockentity.RenderBlockEntitySqueezer;\n\n/**\n * @author rubensworks\n */\npublic class BlockEntitySqueezerConfigClient {\n\n    public void onRegistered(BlockEntitySqueezerConfig config) {\n        config.getMod().getProxy().registerRenderer(config.getInstance(), RenderBlockEntitySqueezer::new);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityVariableStoreConfig.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for the {@link BlockEntityVariablestore}.\n * @author rubensworks\n *\n */\npublic class BlockEntityVariableStoreConfig extends BlockEntityConfigCommon<BlockEntityVariablestore, IntegratedDynamics> {\n\n    public BlockEntityVariableStoreConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"variable_store\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityVariablestore::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_VARIABLE_STORE.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(new BlockEntityVariablestore.CapabilityRegistrar(this::getInstance)::register);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityVariablestore.java",
    "content": "package org.cyclops.integrateddynamics.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\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.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.IVariableContainer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.api.network.INetworkEventListener;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.capability.variablecontainer.VariableContainerDefault;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityCableConnectableInventory;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerVariablestore;\nimport org.cyclops.integrateddynamics.network.VariablestoreNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\n/**\n * A part entity used to store variables.\n * Internally, this also acts as an expression cache\n * @author rubensworks\n */\npublic class BlockEntityVariablestore extends BlockEntityCableConnectableInventory\n        implements IDirtyMarkListener, INetworkEventListener<VariablestoreNetworkElement>, MenuProvider {\n\n    public static final int ROWS = 5;\n    public static final int COLS = 9;\n    public static final int INVENTORY_SIZE = ROWS * COLS;\n\n    private final IVariableContainer variableContainer;\n\n    private boolean shouldSendUpdateEvent = false;\n\n    public BlockEntityVariablestore(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_VARIABLE_STORE.get(), blockPos, blockState, BlockEntityVariablestore.INVENTORY_SIZE, 1);\n        getInventory().addDirtyMarkListener(this);\n        variableContainer = new VariableContainerDefault();\n    }\n\n    public static class CapabilityRegistrar extends BlockEntityCableConnectableInventory.CapabilityRegistrar<BlockEntityVariablestore> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityVariablestore>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, context) -> VanillaContainerWrapper.of(blockEntity.getInventory())\n            );\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, context) -> blockEntity.getNetworkElementProvider()\n            );\n            add(\n                    Capabilities.VariableContainer.BLOCK,\n                    (blockEntity, context) -> blockEntity.getVariableContainer()\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new VariablestoreNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    @Override\n    protected SimpleInventory createInventory(int inventorySize, int stackSize) {\n        return new SimpleInventory(inventorySize, stackSize) {\n            @Override\n            public boolean canPlaceItem(int slot, ItemStack itemStack) {\n                return super.canPlaceItem(slot, itemStack)\n                        && (itemStack.isEmpty() || itemStack.getCapability(Capabilities.VariableFacade.ITEM) != null);\n            }\n        };\n    }\n\n    public IVariableContainer getVariableContainer() {\n        return variableContainer;\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        shouldSendUpdateEvent = true;\n    }\n\n    protected void refreshVariables(boolean sendVariablesUpdateEvent) {\n        variableContainer.refreshVariables(getNetwork(), getInventory(), sendVariablesUpdateEvent, ValueDeseralizationContext.of(getLevel()));\n    }\n\n    @Override\n    public void onDirty() {\n        if(!level.isClientSide()) {\n            refreshVariables(true);\n        }\n    }\n\n    // Make sure that when this TE is loaded, and after the network has been set,\n    // that we trigger a variable update event in the network.\n\n    @Override\n    public void onLoad() {\n        super.onLoad();\n        if(!IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            shouldSendUpdateEvent = true;\n        }\n    }\n\n    @Override\n    public boolean hasEventSubscriptions() {\n        return true;\n    }\n\n    @Override\n    public Set<Class<? extends INetworkEvent>> getSubscribedEvents() {\n        return Sets.<Class<? extends INetworkEvent>>newHashSet(VariableContentsUpdatedEvent.class);\n    }\n\n    @Override\n    public void onEvent(INetworkEvent event, VariablestoreNetworkElement networkElement) {\n        if(event instanceof VariableContentsUpdatedEvent) {\n            refreshVariables(false);\n        }\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n        return new ContainerVariablestore(id, playerInventory, this.getInventory());\n    }\n\n    @Override\n    public Component getDisplayName() {\n        return Component.translatable(\"block.integrateddynamics.variablestore\");\n    }\n\n    public static class Ticker extends BlockEntityCableConnectableInventory.Ticker<BlockEntityVariablestore> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityVariablestore blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.shouldSendUpdateEvent && blockEntity.getNetwork() != null) {\n                blockEntity.shouldSendUpdateEvent = false;\n                blockEntity.refreshVariables(true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/cable/CableDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.cable;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\n\n/**\n * Default implementation of {@link ICable}.\n * @author rubensworks\n */\npublic abstract class CableDefault implements ICable {\n\n    protected abstract boolean isForceDisconnectable();\n    protected abstract EnumFacingMap<Boolean> getForceDisconnected();\n    protected abstract EnumFacingMap<Boolean> getConnected();\n    protected abstract void setChanged();\n    protected abstract void sendUpdate();\n    protected abstract Level getLevel();\n    protected abstract BlockPos getPos();\n\n    protected boolean isForceDisconnected(Direction side) {\n        if (!isForceDisconnectable()) return false;\n        if (!getForceDisconnected().containsKey(side)) return false;\n        return getForceDisconnected().get(side);\n    }\n\n    @Override\n    public boolean canConnect(ICable connector, Direction side) {\n        return !isForceDisconnected(side);\n    }\n\n    @Override\n    public void updateConnections(boolean setChanged) {\n        Level world = getLevel();\n        for (Direction side : Direction.values()) {\n            boolean cableConnected = CableHelpers.canCableConnectTo(world, getPos(), side, this);\n            getConnected().put(side, cableConnected);\n\n            // Remove any already existing force-disconnects for this side.\n            if (!cableConnected && isForceDisconnectable() && this.canConnect(this, side)) {\n                getForceDisconnected().put(side, false);\n            }\n        }\n        if (setChanged) {\n            setChanged();\n            sendUpdate();\n        }\n    }\n\n    @Override\n    public boolean isConnected(Direction side) {\n        if(getPos() == null) {\n            return false;\n        }\n        if(getConnected().isEmpty()) {\n            updateConnections();\n        }\n        return getConnected().containsKey(side) && getConnected().get(side);\n    }\n\n    @Override\n    public void disconnect(Direction side) {\n        if (isForceDisconnectable()) {\n            getForceDisconnected().put(side, true);\n        }\n    }\n\n    @Override\n    public void reconnect(Direction side) {\n        if (isForceDisconnectable()) {\n            getForceDisconnected().remove(side);\n        }\n    }\n\n    @Override\n    public ItemStack getItemStack() {\n        return new ItemStack(RegistryEntries.BLOCK_CABLE.get());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/cable/CableFakeableDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.cable;\n\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\n\n/**\n * Default implementation of {@link ICableFakeable}.\n * @author rubensworks\n */\npublic abstract class CableFakeableDefault implements ICableFakeable {\n\n    private boolean real = true;\n\n    @Override\n    public boolean isRealCable() {\n        return real;\n    }\n\n    @Override\n    public void setRealCable(boolean real) {\n        boolean oldReal = this.real;\n        this.real = real;\n        if (oldReal != this.real) {\n            sendUpdate();\n        }\n    }\n\n    protected abstract void sendUpdate();\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/cable/CableFakeableMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.cable;\n\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\n\n/**\n * Implementation of {@link ICableFakeable} for {@link BlockEntityMultipartTicking}.\n * @author rubensworks\n */\npublic class CableFakeableMultipartTicking extends CableFakeableDefault {\n\n    private final BlockEntityMultipartTicking tile;\n\n    public CableFakeableMultipartTicking(BlockEntityMultipartTicking tile) {\n        this.tile = tile;\n    }\n\n    @Override\n    protected void sendUpdate() {\n        tile.sendUpdate();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/cable/CableTile.java",
    "content": "package org.cyclops.integrateddynamics.capability.cable;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Blocks;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\n\n/**\n * Default implementation of {@link ICable}.\n * @author rubensworks\n */\npublic abstract class CableTile<T extends CyclopsBlockEntity> extends CableDefault {\n\n    protected final T tile;\n\n    public CableTile(T tile) {\n        this.tile = tile;\n    }\n\n    @Override\n    protected void setChanged() {\n        tile.setChanged();\n    }\n\n    @Override\n    protected void sendUpdate() {\n        tile.sendUpdate();\n    }\n\n    @Override\n    protected Level getLevel() {\n        return tile.getLevel();\n    }\n\n    @Override\n    protected BlockPos getPos() {\n        return tile.getBlockPos();\n    }\n\n    @Override\n    public void destroy() {\n        getLevel().setBlock(getPos(), Blocks.AIR.defaultBlockState(), 3);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/cable/CableTileMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.cable;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\n\n/**\n * Default implementation of {@link ICable}.\n * @author rubensworks\n */\npublic class CableTileMultipartTicking extends CableTile<BlockEntityMultipartTicking> {\n\n    public CableTileMultipartTicking(BlockEntityMultipartTicking tile) {\n        super(tile);\n    }\n\n    @Override\n    protected boolean isForceDisconnectable() {\n        return true;\n    }\n\n    @Override\n    protected EnumFacingMap<Boolean> getForceDisconnected() {\n        return tile.getForceDisconnected();\n    }\n\n    @Override\n    protected EnumFacingMap<Boolean> getConnected() {\n        return tile.getConnected();\n    }\n\n    @Override\n    public boolean isForceDisconnected(Direction side) {\n        if(!tile.getCableFakeable().isRealCable() || tile.getPartContainer().hasPart(side)) return true;\n        return super.isForceDisconnected(side);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/dynamiclight/DynamicLightDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.dynamiclight;\n\nimport org.cyclops.integrateddynamics.api.block.IDynamicLight;\n\n/**\n * Default implementation of {@link IDynamicLight}.\n * @author rubensworks\n */\npublic class DynamicLightDefault implements IDynamicLight {\n\n    @Override\n    public void setLightLevel(int level) {\n\n    }\n\n    @Override\n    public int getLightLevel() {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/dynamiclight/DynamicLightTileMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.dynamiclight;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.api.block.IDynamicLight;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\n\n/**\n * Default implementation of {@link IDynamicLight}.\n * @author rubensworks\n */\npublic class DynamicLightTileMultipartTicking implements IDynamicLight {\n\n    private final BlockEntityMultipartTicking tile;\n    private final Direction side;\n\n    public DynamicLightTileMultipartTicking(BlockEntityMultipartTicking tile, Direction side) {\n        this.tile = tile;\n        this.side = side;\n    }\n\n    protected EnumFacingMap<Integer> getLightLevels() {\n        return tile.getLightLevels();\n    }\n\n    @Override\n    public void setLightLevel(int level) {\n        if(!tile.getLevel().isClientSide()) {\n            boolean sendUpdate = false;\n            EnumFacingMap<Integer> lightLevels = getLightLevels();\n            if(lightLevels.containsKey(side)) {\n                if(lightLevels.get(side) != level) {\n                    sendUpdate = true;\n                    lightLevels.put(side, level);\n                }\n            } else {\n                sendUpdate = true;\n                lightLevels.put(side, level);\n            }\n            if(sendUpdate) {\n                tile.updateLightInfo();\n            }\n        }\n    }\n\n    @Override\n    public int getLightLevel() {\n        EnumFacingMap<Integer> lightLevels = getLightLevels();\n        if(lightLevels.containsKey(side)) {\n            return lightLevels.get(side);\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/dynamicredstone/DynamicRedstoneDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.dynamicredstone;\n\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\n\n/**\n * Default implementation of {@link IDynamicRedstone}.\n * @author rubensworks\n */\npublic class DynamicRedstoneDefault implements IDynamicRedstone {\n\n    @Override\n    public void setRedstoneLevel(int level, boolean direct) {\n\n    }\n\n    @Override\n    public int getRedstoneLevel() {\n        return 0;\n    }\n\n    @Override\n    public boolean isDirect() {\n        return false;\n    }\n\n    @Override\n    public void setAllowRedstoneInput(boolean allow) {\n\n    }\n\n    @Override\n    public boolean isAllowRedstoneInput() {\n        return false;\n    }\n\n    @Override\n    public void setLastPulseValue(int value) {\n\n    }\n\n    @Override\n    public int getLastPulseValue() {\n        return 0;\n    }\n\n    @Override\n    public void setScheduledPulseRemaining(int ticks) {\n\n    }\n\n    @Override\n    public int getScheduledPulseRemaining() {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/dynamicredstone/DynamicRedstoneHolderGlobal.java",
    "content": "package org.cyclops.integrateddynamics.capability.dynamicredstone;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;\nimport net.minecraft.world.level.redstone.Orientation;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\n\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class DynamicRedstoneHolderGlobal {\n\n    private static final DynamicRedstoneHolderGlobal _INSTANCE = new DynamicRedstoneHolderGlobal();\n\n    private Map<DimPos, EnumFacingMap<Integer>> redstoneLevels = Maps.newHashMap();\n    private Map<DimPos, EnumFacingMap<Boolean>> redstoneStrong = Maps.newHashMap();\n\n    private DynamicRedstoneHolderGlobal() {\n\n    }\n\n    public static DynamicRedstoneHolderGlobal getInstance() {\n        return _INSTANCE;\n    }\n\n    public boolean hasLevels() {\n        return !this.redstoneLevels.isEmpty();\n    }\n\n    public IDynamicRedstone getDynamicRedstone(DimPos dimPos, Direction side) {\n        return new DynamicRedstoneVirtual(this, dimPos, side);\n    }\n\n    public static class DynamicRedstoneVirtual implements IDynamicRedstone {\n\n        private final DynamicRedstoneHolderGlobal holder;\n        private final DimPos dimPos;\n        private final Direction side;\n\n        public DynamicRedstoneVirtual(DynamicRedstoneHolderGlobal holder, DimPos dimPos, Direction side) {\n            this.holder = holder;\n            this.dimPos = dimPos;\n            this.side = side;\n        }\n\n        @Override\n        public void setRedstoneLevel(int level, boolean direct) {\n            EnumFacingMap<Integer> redstoneLevels = this.holder.redstoneLevels.get(this.dimPos);\n            if (redstoneLevels == null) {\n                redstoneLevels = EnumFacingMap.newMap();\n                this.holder.redstoneLevels.put(this.dimPos, redstoneLevels);\n            }\n            EnumFacingMap<Boolean> redstoneStrongs = this.holder.redstoneStrong.get(this.dimPos);\n            if (redstoneStrongs == null) {\n                redstoneStrongs = EnumFacingMap.newMap();\n                this.holder.redstoneStrong.put(this.dimPos, redstoneStrongs);\n            }\n\n            boolean sendUpdate = false;\n            boolean sendUpdateStrong = false;\n            if(redstoneLevels.containsKey(side)) {\n                if(redstoneLevels.get(side) != level) {\n                    sendUpdate = true;\n                    if (level < 0) {\n                        redstoneLevels.remove(side);\n                    } else {\n                        redstoneLevels.put(side, level);\n                    }\n                }\n            } else {\n                sendUpdate = true;\n                if (level < 0) {\n                    redstoneLevels.remove(side);\n                } else {\n                    redstoneLevels.put(side, level);\n                }\n            }\n            if(redstoneStrongs.containsKey(side)) {\n                if(redstoneStrongs.get(side) != direct) {\n                    sendUpdateStrong = true;\n                    sendUpdate = true;\n                    if (level < 0) {\n                        redstoneStrongs.remove(side);\n                    } else {\n                        redstoneStrongs.put(side, direct);\n                    }\n                }\n            } else {\n                sendUpdateStrong = true;\n                sendUpdate = true;\n                if (level < 0) {\n                    redstoneStrongs.remove(side);\n                } else {\n                    redstoneStrongs.put(side, direct);\n                }\n            }\n\n\n            if (redstoneLevels.isEmpty()) {\n                this.holder.redstoneLevels.remove(this.dimPos);\n            }\n            if (redstoneStrongs.isEmpty()) {\n                this.holder.redstoneStrong.remove(this.dimPos);\n            }\n\n            if(sendUpdate) {\n                updateRedstoneInfo(direct || sendUpdateStrong);\n            }\n        }\n\n        public void updateRedstoneInfo(boolean strongPower) {\n            Level level = this.dimPos.getLevel(false);\n            if (level != null) {\n                BlockPos pos = this.dimPos.getBlockPos();\n                if (level.isLoaded(pos.relative(side.getOpposite()))) {\n                    BlockState blockState = level.getBlockState(pos);\n                    Orientation orientation = ExperimentalRedstoneUtils.initialOrientation(level, side.getOpposite(), null);\n                    level.neighborChanged(pos.relative(side.getOpposite()), blockState.getBlock(), orientation);\n                    if (strongPower) {\n                        // When we are emitting a strong power, also update all neighbours of the target\n                        level.updateNeighborsAt(pos.relative(side.getOpposite()), blockState.getBlock());\n                    }\n                }\n            }\n        }\n\n        @Override\n        public int getRedstoneLevel() {\n            EnumFacingMap<Integer> redstoneLevels = this.holder.redstoneLevels.get(this.dimPos);\n            if(redstoneLevels != null) {\n                return redstoneLevels.getOrDefault(side, -1);\n            }\n            return -1;\n        }\n\n        @Override\n        public boolean isDirect() {\n            EnumFacingMap<Boolean> redstoneStrongs = this.holder.redstoneStrong.get(this.dimPos);\n            if(redstoneStrongs != null) {\n                return redstoneStrongs.getOrDefault(side, false);\n            }\n            return false;\n        }\n\n        @Override\n        public void setAllowRedstoneInput(boolean allow) {\n            // Not required\n        }\n\n        @Override\n        public boolean isAllowRedstoneInput() {\n            // Not required\n            return false;\n        }\n\n        @Override\n        public void setLastPulseValue(int value) {\n            // Not required\n        }\n\n        @Override\n        public int getLastPulseValue() {\n            // Not required\n            return 0;\n        }\n\n        @Override\n        public void setScheduledPulseRemaining(int ticks) {\n            // Not required\n        }\n\n        @Override\n        public int getScheduledPulseRemaining() {\n            // Not required\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/dynamicredstone/DynamicRedstoneTileMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.dynamicredstone;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\n\n/**\n * Default implementation of {@link IDynamicRedstone}.\n * @author rubensworks\n */\npublic class DynamicRedstoneTileMultipartTicking implements IDynamicRedstone {\n\n    private final BlockEntityMultipartTicking tile;\n    private final Direction side;\n\n    public DynamicRedstoneTileMultipartTicking(BlockEntityMultipartTicking tile, Direction side) {\n        this.tile = tile;\n        this.side = side;\n    }\n\n    protected EnumFacingMap<Integer> getRedstoneLevels() {\n        return tile.getRedstoneLevels();\n    }\n\n    protected EnumFacingMap<Boolean> getRedstoneInputs() {\n        return tile.getRedstoneInputs();\n    }\n\n    protected EnumFacingMap<Boolean> getRedstoneStrong() {\n        return tile.getRedstoneStrong();\n    }\n\n    @Override\n    public void setRedstoneLevel(int level, boolean direct) {\n        if(!tile.getLevel().isClientSide()) {\n            EnumFacingMap<Integer> redstoneLevels = getRedstoneLevels();\n            EnumFacingMap<Boolean> redstoneStrongs = getRedstoneStrong();\n            boolean sendUpdate = false;\n            boolean sendUpdateStrong = false;\n            if(redstoneLevels.containsKey(side)) {\n                if(redstoneLevels.get(side) != level) {\n                    sendUpdate = true;\n                    redstoneLevels.put(side, level);\n                }\n            } else {\n                sendUpdate = true;\n                redstoneLevels.put(side, level);\n            }\n            if(redstoneStrongs.containsKey(side)) {\n                if(redstoneStrongs.get(side) != direct) {\n                    sendUpdateStrong = true;\n                    sendUpdate = true;\n                    redstoneStrongs.put(side, direct);\n                }\n            } else {\n                sendUpdateStrong = true;\n                sendUpdate = true;\n                redstoneStrongs.put(side, direct);\n            }\n            if(sendUpdate) {\n                tile.updateRedstoneInfo(side, direct || sendUpdateStrong);\n            }\n        }\n    }\n\n    @Override\n    public int getRedstoneLevel() {\n        EnumFacingMap<Integer> redstoneLevels = getRedstoneLevels();\n        if(redstoneLevels.containsKey(side)) {\n            return redstoneLevels.get(side);\n        }\n        return -1;\n    }\n\n    @Override\n    public boolean isDirect() {\n        EnumFacingMap<Boolean> redstoneStrongs = getRedstoneStrong();\n        if(redstoneStrongs.containsKey(side)) {\n            return redstoneStrongs.get(side);\n        }\n        return false;\n    }\n\n    @Override\n    public void setAllowRedstoneInput(boolean allow) {\n        EnumFacingMap<Boolean> redstoneInputs = getRedstoneInputs();\n        redstoneInputs.put(side, allow);\n    }\n\n    @Override\n    public boolean isAllowRedstoneInput() {\n        EnumFacingMap<Boolean> redstoneInputs = getRedstoneInputs();\n        if(redstoneInputs.containsKey(side)) {\n            return redstoneInputs.get(side);\n        }\n        return false;\n    }\n\n    @Override\n    public void setLastPulseValue(int value) {\n        EnumFacingMap<Integer> pulses = tile.getLastRedstonePulses();\n        pulses.put(side, value);\n    }\n\n    @Override\n    public int getLastPulseValue() {\n        EnumFacingMap<Integer> pulses = tile.getLastRedstonePulses();\n        if (pulses.containsKey(side)) {\n            return pulses.get(side);\n        }\n        return 0;\n    }\n\n    @Override\n    public void setScheduledPulseRemaining(int ticks) {\n        EnumFacingMap<Integer> scheduledPulses = tile.getScheduledPulseRemaining();\n        if (ticks > 0) {\n            scheduledPulses.put(side, ticks);\n        } else {\n            scheduledPulses.remove(side);\n        }\n    }\n\n    @Override\n    public int getScheduledPulseRemaining() {\n        EnumFacingMap<Integer> scheduledPulses = tile.getScheduledPulseRemaining();\n        if (scheduledPulses.containsKey(side)) {\n            return scheduledPulses.get(side);\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/energystorage/EnergyStorageItemBlockEnergyContainer.java",
    "content": "package org.cyclops.integrateddynamics.capability.energystorage;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Block;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport net.neoforged.neoforge.transfer.transaction.SnapshotJournal;\nimport net.neoforged.neoforge.transfer.transaction.TransactionContext;\nimport org.cyclops.cyclopscore.RegistryEntries;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryBase;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryConfig;\nimport org.cyclops.integrateddynamics.core.item.ItemBlockEnergyContainer;\n\nimport javax.annotation.Nullable;\n\n/**\n * Energy Battery implementation for ItemBlock's.\n * @author rubensworks\n */\npublic class EnergyStorageItemBlockEnergyContainer implements IEnergyStorageCapacity, IEnergyStorageMutable {\n\n    private final ItemBlockEnergyContainer itemBlockEnergyContainer;\n    private final ItemStack itemStack;\n    private final ItemAccess itemAccess;\n    private final int rate;\n    private final Journal journal;\n\n    public EnergyStorageItemBlockEnergyContainer(ItemBlockEnergyContainer itemBlockEnergyContainer, ItemStack itemStack, ItemAccess itemAccess, int rate) {\n        this.itemBlockEnergyContainer = itemBlockEnergyContainer;\n        this.itemStack = itemStack;\n        this.itemAccess = itemAccess;\n        this.rate = rate;\n        this.journal = new Journal();\n\n        if (!this.itemStack.has(RegistryEntries.COMPONENT_ENERGY_STORAGE)) {\n            setItemStackEnergy(itemStack, 0, null);\n        }\n    }\n\n    public EnergyStorageItemBlockEnergyContainer(ItemBlockEnergyContainer itemBlockEnergyContainer, ItemStack itemStack, ItemAccess itemAccess) {\n        this(itemBlockEnergyContainer, itemStack, itemAccess, Integer.MAX_VALUE);\n    }\n\n    public int getRate() {\n        return rate;\n    }\n\n    public boolean isCreative() {\n        Block block = itemBlockEnergyContainer.get();\n        return block instanceof BlockEnergyBatteryBase && ((BlockEnergyBatteryBase) block).isCreative();\n    }\n\n    protected int getEnergyStoredSingular() {\n        if(isCreative()) return Integer.MAX_VALUE;\n        return itemStack.get(RegistryEntries.COMPONENT_ENERGY_STORAGE);\n    }\n\n    @Override\n    public int getAmountAsInt() {\n        return IModHelpers.get().getBaseHelpers().multiplySafe(getEnergyStoredSingular(), this.itemStack.getCount());\n    }\n\n    @Override\n    public long getAmountAsLong() {\n        return ((long) getEnergyStoredSingular()) * this.itemStack.getCount();\n    }\n\n    public int getMaxEnergyStoredSingular() {\n        if(isCreative()) return Integer.MAX_VALUE;\n        if (!itemStack.has(RegistryEntries.COMPONENT_CAPACITY)) {\n            return BlockEnergyBatteryConfig.capacity;\n        }\n        return itemStack.get(RegistryEntries.COMPONENT_CAPACITY);\n    }\n\n    @Override\n    public int getCapacityAsInt() {\n        return IModHelpers.get().getBaseHelpers().multiplySafe(getMaxEnergyStoredSingular(), this.itemStack.getCount());\n    }\n\n    @Override\n    public long getCapacityAsLong() {\n        return ((long) getMaxEnergyStoredSingular()) * this.itemStack.getCount();\n    }\n\n    @Override\n    public int insert(int energy, TransactionContext transaction) {\n        if(isCreative()) return 0;\n        int stackSize = this.itemStack.getCount();\n        if (stackSize == 0) return 0;\n        energy /= stackSize;\n        energy = Math.min(energy, getRate());\n        int stored = getEnergyStoredSingular();\n        int energyReceived = Math.min(getMaxEnergyStoredSingular() - stored, energy);\n        this.journal.updateSnapshots(transaction);\n        setItemStackEnergy(itemStack, stored + energyReceived, transaction);\n        return energyReceived * stackSize;\n    }\n\n    @Override\n    public int extract(int energy, TransactionContext transaction) {\n        if(isCreative()) return energy;\n        int stackSize = this.itemStack.getCount();\n        if (stackSize == 0) return energy;\n        energy /= stackSize;\n        energy = Math.min(energy, getRate());\n        int stored = getEnergyStoredSingular();\n        int newEnergy = Math.max(stored - energy, 0);\n        this.journal.updateSnapshots(transaction);\n        setItemStackEnergy(itemStack, newEnergy, transaction);\n        return (stored - newEnergy) * stackSize;\n    }\n\n    protected void setItemStackEnergy(ItemStack itemStack, int energy, @Nullable TransactionContext transaction) {\n        if(isCreative()) return;\n        itemStack.set(RegistryEntries.COMPONENT_ENERGY_STORAGE, energy);\n        if (transaction != null) {\n            itemAccess.exchange(ItemResource.of(itemStack), itemAccess.getAmount(), transaction);\n        }\n    }\n\n    @Override\n    public void setCapacity(int capacity) {\n        if (capacity == BlockEnergyBatteryConfig.capacity) {\n            itemStack.remove(RegistryEntries.COMPONENT_CAPACITY);\n        } else {\n            itemStack.set(RegistryEntries.COMPONENT_CAPACITY, capacity);\n        }\n    }\n\n    @Override\n    public void setEnergy(int energy) {\n        setItemStackEnergy(itemStack, energy, null);\n    }\n\n    public class Journal extends SnapshotJournal<Integer> {\n\n        @Override\n        protected Integer createSnapshot() {\n            return getEnergyStoredSingular();\n        }\n\n        @Override\n        protected void revertToSnapshot(Integer snapshot) {\n            setItemStackEnergy(itemStack, snapshot, null);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/energystorage/IEnergyStorageCapacity.java",
    "content": "package org.cyclops.integrateddynamics.capability.energystorage;\n\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\n\n/**\n * An energy storage with a mutable capacity.\n * @author rubensworks\n */\npublic interface IEnergyStorageCapacity extends EnergyHandler {\n\n    public void setCapacity(int capacity);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/energystorage/IEnergyStorageMutable.java",
    "content": "package org.cyclops.integrateddynamics.capability.energystorage;\n\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\n\n/**\n * An energy storage with a mutable energy level.\n * @author rubensworks\n */\npublic interface IEnergyStorageMutable extends EnergyHandler {\n\n    public void setEnergy(int energy);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/energystorage/SimpleEnergyHandlerCapacity.java",
    "content": "package org.cyclops.integrateddynamics.capability.energystorage;\n\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;\n\n/**\n * @author rubensworks\n */\npublic class SimpleEnergyHandlerCapacity extends SimpleEnergyHandler implements IEnergyStorageCapacity {\n    public SimpleEnergyHandlerCapacity(int capacity) {\n        super(capacity);\n    }\n\n    public SimpleEnergyHandlerCapacity(int capacity, int maxTransfer) {\n        super(capacity, maxTransfer);\n    }\n\n    public SimpleEnergyHandlerCapacity(int capacity, int maxInsert, int maxExtract) {\n        super(capacity, maxInsert, maxExtract);\n    }\n\n    public SimpleEnergyHandlerCapacity(int capacity, int maxInsert, int maxExtract, int energy) {\n        super(capacity, maxInsert, maxExtract, energy);\n    }\n\n    @Override\n    public void serialize(ValueOutput output) {\n        super.serialize(output);\n        output.putInt(\"capacity\", capacity);\n    }\n\n    @Override\n    public void deserialize(ValueInput input) {\n        super.deserialize(input);\n        capacity = Math.max(0, input.getIntOr(\"capacity\", capacity));\n    }\n\n    @Override\n    public void setCapacity(int capacity) {\n        this.capacity = capacity;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/facadeable/FacadeableDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.facadeable;\n\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.integrateddynamics.api.block.IFacadeable;\n\nimport javax.annotation.Nullable;\n\n/**\n * Default implementation of {@link IFacadeable}.\n * @author rubensworks\n */\npublic class FacadeableDefault implements IFacadeable {\n\n    @Override\n    public boolean hasFacade() {\n        return false;\n    }\n\n    @Override\n    public BlockState getFacade() {\n        return null;\n    }\n\n    @Override\n    public void setFacade(@Nullable BlockState blockState) {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/facadeable/FacadeableTileMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.facadeable;\n\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.block.IFacadeable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\n\nimport javax.annotation.Nullable;\n\n/**\n * Default implementation of {@link IFacadeable}.\n * @author rubensworks\n */\npublic class FacadeableTileMultipartTicking implements IFacadeable {\n\n    private final BlockEntityMultipartTicking tile;\n\n    public FacadeableTileMultipartTicking(BlockEntityMultipartTicking tile) {\n        this.tile = tile;\n    }\n\n    @Override\n    public boolean hasFacade() {\n        return tile.getFacadeBlockTag() != null;\n    }\n\n    @Override\n    public BlockState getFacade() {\n        if(!hasFacade()) {\n            return null;\n        }\n        return IModHelpers.get().getBlockHelpers().deserializeBlockState(ValueDeseralizationContext.of(tile.getLevel()).holderLookupProvider().lookupOrThrow(Registries.BLOCK), tile.getFacadeBlockTag());\n    }\n\n    @Override\n    public void setFacade(@Nullable BlockState blockState) {\n        if(blockState == null) {\n            tile.setFacadeBlockTag(null);\n        } else {\n            tile.setFacadeBlockTag(IModHelpers.get().getBlockHelpers().serializeBlockState(blockState));\n        }\n        tile.setForceLightCheckAtClient(true);\n        tile.setChanged();\n        tile.sendUpdate();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/ingredient/IngredientComponentCapabilities.java",
    "content": "package org.cyclops.integrateddynamics.capability.ingredient;\n\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.neoforge.capabilities.ICapabilityProvider;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.capability.IngredientComponentCapabilityAttacherAdapter;\nimport org.cyclops.commoncapabilities.api.ingredient.capability.IngredientComponentCapabilityAttacherManager;\nimport org.cyclops.cyclopscore.modcompat.capabilities.DefaultCapabilityProvider;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.ingredient.capability.IPositionedAddonsNetworkIngredientsHandler;\n\nimport java.util.Optional;\n\n/**\n * Value handlers for ingredient components.\n * @author rubensworks\n */\npublic class IngredientComponentCapabilities {\n\n    public static final Identifier INGREDIENT_ITEMSTACK_NAME = Identifier.fromNamespaceAndPath(\"minecraft\", \"itemstack\");\n    public static final Identifier INGREDIENT_FLUIDSTACK_NAME = Identifier.fromNamespaceAndPath(\"minecraft\", \"fluidstack\");\n    public static final Identifier INGREDIENT_ENERGY_NAME = Identifier.fromNamespaceAndPath(\"minecraft\", \"energy\");\n\n    public static void load() {\n        IngredientComponentCapabilityAttacherManager attacherManager = new IngredientComponentCapabilityAttacherManager();\n\n        // Network handler\n        attacherManager.addAttacher(new IngredientComponentCapabilityAttacherAdapter<Integer, Boolean>(INGREDIENT_ENERGY_NAME, Capabilities.PositionedAddonsNetworkIngredientsHandler.INGREDIENT) {\n            @Override\n            public ICapabilityProvider<IngredientComponent<Integer, Boolean>, Void, IPositionedAddonsNetworkIngredientsHandler<Integer, Boolean>> createCapabilityProvider(IngredientComponent<Integer, Boolean> ingredientComponent) {\n                return new DefaultCapabilityProvider<>(network -> (Optional) network.getCapability(Capabilities.EnergyNetwork.NETWORK));\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/network/NetworkCapabilityConstructors.java",
    "content": "package org.cyclops.integrateddynamics.capability.network;\n\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.modcompat.capabilities.DefaultCapabilityProvider;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.network.AttachCapabilitiesEventNetwork;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.core.network.EnergyNetwork;\nimport org.cyclops.integrateddynamics.core.network.PartNetwork;\n\n/**\n * Constructor event for network capabilities.\n * @author rubensworks\n */\npublic class NetworkCapabilityConstructors {\n\n    @SubscribeEvent\n    public void onNetworkLoad(AttachCapabilitiesEventNetwork event) {\n        INetwork network = event.getNetwork();\n        PartNetwork partNetwork = new PartNetwork();\n        EnergyNetwork energyNetwork = new EnergyNetwork(IngredientComponent.ENERGY);\n        energyNetwork.setNetwork(network);\n        EnergyHandler energyChannel = energyNetwork.getChannelExternal(net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK, IPositionedAddonsNetwork.DEFAULT_CHANNEL);\n\n        event.register(Capabilities.PartNetwork.NETWORK, new DefaultCapabilityProvider<>(partNetwork));\n        event.register(Capabilities.EnergyNetwork.NETWORK, new DefaultCapabilityProvider<>(energyNetwork));\n        event.register(Capabilities.Energy.NETWORK, new DefaultCapabilityProvider<>(energyChannel));\n\n        event.addFullNetworkListener(partNetwork);\n        event.addFullNetworkListener(energyNetwork);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/network/NetworkCarrierDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.network;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkCarrier;\n\nimport javax.annotation.Nullable;\n\n/**\n * Default implementation of {@link INetworkCarrier}.\n * @author rubensworks\n */\npublic class NetworkCarrierDefault implements INetworkCarrier {\n\n    private INetwork network;\n\n    @Override\n    public void setNetwork(@Nullable INetwork network) {\n        this.network = network;\n    }\n\n    @Nullable\n    @Override\n    public INetwork getNetwork() {\n        return network;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/networkelementprovider/NetworkElementProviderEmpty.java",
    "content": "package org.cyclops.integrateddynamics.capability.networkelementprovider;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * An dummy network element provider implementation.\n * @author rubensworks\n */\npublic class NetworkElementProviderEmpty implements INetworkElementProvider {\n    @Override\n    public Collection<INetworkElement> createNetworkElements(Level world, BlockPos blockPos) {\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/networkelementprovider/NetworkElementProviderPartContainer.java",
    "content": "package org.cyclops.integrateddynamics.capability.networkelementprovider;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Network element provider for {@link IPartContainer}.\n * @author rubensworks\n */\npublic class NetworkElementProviderPartContainer implements INetworkElementProvider {\n\n    private final IPartContainer partContainer;\n\n    public NetworkElementProviderPartContainer(IPartContainer partContainer) {\n        this.partContainer = partContainer;\n    }\n\n    @Override\n    public Collection<INetworkElement> createNetworkElements(Level world, BlockPos blockPos) {\n        Set<INetworkElement> sidedElements = Sets.newHashSet();\n        for(Map.Entry<Direction, IPartType<?, ?>> entry : partContainer.getParts().entrySet()) {\n            sidedElements.add(entry.getValue().createNetworkElement(partContainer, DimPos.of(world, blockPos), entry.getKey()));\n        }\n        return sidedElements;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/networkelementprovider/NetworkElementProviderSingleton.java",
    "content": "package org.cyclops.integrateddynamics.capability.networkelementprovider;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * A network element provider for a single element.\n * @author rubensworks\n */\npublic abstract class NetworkElementProviderSingleton implements INetworkElementProvider {\n    @Override\n    public Collection<INetworkElement> createNetworkElements(Level world, BlockPos blockPos) {\n        return Collections.singleton(createNetworkElement(world, blockPos));\n    }\n\n    public abstract INetworkElement createNetworkElement(Level world, BlockPos blockPos);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/partcontainer/PartContainerDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.partcontainer;\n\nimport com.google.common.base.Function;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\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.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Default implementation of an {@link IPartContainer}.\n * @author rubensworks\n */\npublic abstract class PartContainerDefault implements IPartContainer {\n\n    protected final EnumFacingMap<PartHelpers.PartStateHolder<?, ?>> partData = EnumFacingMap.newMap();\n\n    @Override\n    public void update() {\n        if(!IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            // Loop over all part states to check their dirtiness\n            for (PartHelpers.PartStateHolder<?, ?> partStateHolder : partData.values()) {\n                if (partStateHolder.getState().isDirtyAndReset()) {\n                    setChanged();\n                }\n                if (partStateHolder.getState().isUpdateAndReset()) {\n                    sendUpdate();\n                }\n            }\n        }\n    }\n\n    @Override\n    public DimPos getPosition() {\n        return DimPos.of(getLevel(), getPos());\n    }\n\n    @Override\n    public Map<Direction, IPartType<?, ?>> getParts() {\n        return Maps.transformValues(partData, new Function<PartHelpers.PartStateHolder<?, ?>, IPartType<?, ?>>() {\n            @Nullable\n            @Override\n            public IPartType<?, ?> apply(@Nullable PartHelpers.PartStateHolder<?, ?> input) {\n                return input.getPart();\n            }\n        });\n    }\n\n    @Override\n    public boolean hasParts() {\n        return !partData.isEmpty();\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> boolean canAddPart(Direction side, IPartType<P, S> part) {\n        return !hasPart(side);\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>>void setPart(final Direction side, final IPartType<P, S> part, final IPartState<P> partState) {\n        PartHelpers.setPart(getNetwork(), getLevel(), getPos(), side, Objects.requireNonNull(part),\n                Objects.requireNonNull(partState), new PartHelpers.IPartStateHolderCallback() {\n                    @Override\n                    public void onSet(PartHelpers.PartStateHolder<?, ?> partStateHolder) {\n                        partData.put(side, PartHelpers.PartStateHolder.of(part, partState));\n                        sendUpdate();\n                    }\n                });\n        onPartsChanged();\n    }\n\n    @Override\n    public IPartType getPart(Direction side) {\n        PartHelpers.PartStateHolder<?, ?> partStateHolder = partData.get(side);\n        return partStateHolder != null ? partStateHolder.getPart() : null;\n    }\n\n    @Override\n    public boolean hasPart(Direction side) {\n        return partData.containsKey(side);\n    }\n\n    @Override\n    public IPartType removePart(Direction side, Player player, boolean dropMainElement, boolean saveState) {\n        PartHelpers.PartStateHolder<?, ?> partStateHolder = partData.get(side); // Don't remove the state just yet! We might need it in network removal.\n        if(partStateHolder == null) {\n            IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, \"Attempted to remove a part at a side where no part was.\");\n            return null;\n        } else {\n            IPartType removed = partStateHolder.getPart();\n            if (getNetwork() != null) {\n                INetworkElement networkElement = removed.createNetworkElement(this, getPosition(), side);\n                networkElement.onPreRemoved(getNetwork());\n                if(!getNetwork().removeNetworkElementPre(networkElement)) {\n                    return null;\n                }\n\n                // Don't drop main element when in creative mode\n                if(player != null && player.isCreative()) {\n                    dropMainElement = false;\n                }\n\n                // Drop all parts types as item.\n                List<ItemStack> itemStacks = Lists.newLinkedList();\n                BlockState blockState = getLevel().getBlockState(getPos());\n                BlockEntity blockEntity = getLevel().getBlockEntity(getPos());\n                networkElement.addDrops(blockState, blockEntity, itemStacks, dropMainElement, saveState);\n                for(ItemStack itemStack : itemStacks) {\n                    if(player != null) {\n                        IModHelpers.get().getItemStackHelpers().spawnItemStackToPlayer(getLevel(), getPos(), itemStack, player);\n                    } else {\n                        IModHelpers.get().getItemStackHelpers().spawnItemStack(getLevel(), getPos(), itemStack);\n                    }\n                }\n\n                // Remove the element from the network.\n                getNetwork().removeNetworkElementPost(networkElement, blockState, blockEntity);\n\n                // Finally remove the part data from this part.\n                IPartType ret = partData.remove(side).getPart();\n\n                networkElement.onPostRemoved(getNetwork());\n\n                onPartsChanged();\n                return ret;\n            } else if (dropMainElement) {\n                ItemStack itemStack = removed.getItemStack(ValueDeseralizationContext.of(player.level()), new PartPathElement(getPos()), partStateHolder.getState(), saveState);\n                if(player != null) {\n                    if (!player.isCreative()) {\n                        IModHelpers.get().getItemStackHelpers().spawnItemStackToPlayer(getLevel(), getPos(), itemStack, player);\n                    }\n                } else {\n                    IModHelpers.get().getItemStackHelpers().spawnItemStack(getLevel(), getPos(), itemStack);\n                }\n            }\n            // Finally remove the part data from this part.\n            IPartType ret = partData.remove(side).getPart();\n            onPartsChanged();\n            return ret;\n        }\n    }\n\n    @Override\n    public void setPartState(Direction side, IPartState partState) throws PartStateException {\n        if(!hasPart(side)) {\n            throw new PartStateException(getPosition(), side);\n        }\n        partData.put(side, PartHelpers.PartStateHolder.of(getPart(side), partState));\n        onPartsChanged();\n    }\n\n    @Override\n    public IPartState getPartState(Direction side) throws PartStateException {\n        synchronized (partData) {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = partData.get(side);\n            if (partStateHolder == null) {\n                throw new PartStateException(getPosition(), side);\n            }\n            return partStateHolder.getState();\n        }\n    }\n\n    @Override\n    public <T> Optional<T> getCapability(PartCapability<T> partCapability, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n        if (hasPart(target.getCenter().getSide())) {\n            IPartState partState = getPartState(target.getCenter().getSide());\n            if (partState != null) {\n                return partState.getCapability(\n                        getPart(target.getCenter().getSide()),\n                        partCapability,\n                        network,\n                        partNetwork,\n                        target\n                );\n            }\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public void toValueOutput(ValueOutput valueOutput) {\n        PartHelpers.serializeParts(valueOutput, getPos(), this.partData);\n    }\n\n    @Override\n    public void fromValueInput(ValueInput valueInput) {\n        synchronized (this.partData) {\n            PartHelpers.deserializeParts(valueInput, getNetwork(), getPos(), this.partData, getLevel());\n        }\n    }\n\n    protected void onPartsChanged() {\n        setChanged();\n        sendUpdate();\n    }\n\n    protected abstract void setChanged();\n    protected abstract void sendUpdate();\n    protected abstract Level getLevel();\n    protected abstract BlockPos getPos();\n    protected abstract INetwork getNetwork();\n\n    protected Optional<IPartNetwork> getPartNetwork() {\n        return NetworkHelpers.getPartNetwork(getNetwork());\n    }\n\n    /**\n     * @return The raw part data.\n     */\n    public EnumFacingMap<PartHelpers.PartStateHolder<?, ?>> getPartData() {\n        return this.partData;\n    }\n\n    /**\n     * Override the part data.\n     * @param partData The raw part data.\n     */\n    public void setPartData(Map<Direction, PartHelpers.PartStateHolder<?, ?>> partData) {\n        this.partData.clear();\n        this.partData.putAll(partData);\n    }\n\n    /**\n     * Reset the part data without signaling any neighbours or the network.\n     * Is used in block conversion.\n     */\n    public void silentResetPartData() {\n        this.partData.clear();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/partcontainer/PartContainerTileMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.partcontainer;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.block.BlockCable;\nimport org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\n\nimport javax.annotation.Nullable;\n\n/**\n * Implementation of an {@link IPartContainer} for a part entity.\n * @author rubensworks\n */\npublic class PartContainerTileMultipartTicking extends PartContainerDefault {\n\n    private final BlockEntityMultipartTicking tile;\n\n    public PartContainerTileMultipartTicking(BlockEntityMultipartTicking tile) {\n        this.tile = tile;\n    }\n\n    protected BlockEntityMultipartTicking getTile() {\n        return tile;\n    }\n\n    @Override\n    protected void setChanged() {\n        getTile().setChanged();\n    }\n\n    @Override\n    protected void sendUpdate() {\n        getTile().sendUpdate();\n    }\n\n    @Override\n    protected Level getLevel() {\n        return getTile().getLevel();\n    }\n\n    @Override\n    protected BlockPos getPos() {\n        return getTile().getBlockPos();\n    }\n\n    @Override\n    protected INetwork getNetwork() {\n        return getTile().getNetwork();\n    }\n\n    @Nullable\n    @Override\n    public Direction getWatchingSide(Level world, BlockPos pos, Player player) {\n        BlockRayTraceResultComponent rayTraceResult = ((BlockCable) world.getBlockState(pos).getBlock())\n                .getSelectedShape(world.getBlockState(pos), world, pos, CollisionContext.of(player))\n                .rayTrace(pos, player);\n        if(rayTraceResult != null) {\n            return rayTraceResult.getComponent().getRaytraceDirection();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/path/PathElementCable.java",
    "content": "package org.cyclops.integrateddynamics.capability.path;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\n\nimport java.util.Set;\n\n/**\n * Implementation of {@link IPathElement} for cables.\n * @author rubensworks\n */\npublic abstract class PathElementCable extends PathElementDefault {\n\n    protected abstract ICable getCable();\n\n    @Override\n    public Set<ISidedPathElement> getReachableElements() {\n        Set<ISidedPathElement> elements = Sets.newHashSet();\n        BlockPos pos = getPosition().getBlockPos();\n        for (Direction side : Direction.values()) {\n            if (getCable().isConnected(side)) {\n                BlockPos posOffset = pos.relative(side);\n                Direction pathElementSide = side.getOpposite();\n                IPathElement pathElement = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(getPosition().getLevel(true), posOffset, pathElementSide, Capabilities.PathElement.BLOCK).orElse(null);\n                if (pathElement == null) {\n                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, String.format(\"The position at %s was incorrectly marked \" +\n                            \"as reachable as path element by %s at %s side %s.\", posOffset, getCable(), pos, side));\n                } else {\n                    elements.add(SidedPathElement.of(pathElement, pathElementSide));\n                }\n            }\n        }\n        return elements;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/path/PathElementDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.path;\n\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\n\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * Default implementation of {@link IPathElement}.\n * @author rubensworks\n */\npublic abstract class PathElementDefault implements IPathElement {\n\n    @Override\n    public Set<ISidedPathElement> getReachableElements() {\n        return Collections.emptySet();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        return o instanceof IPathElement && compareTo((IPathElement)o) == 0;\n    }\n\n    @Override\n    public int compareTo(IPathElement o) {\n        return getPosition().compareTo(o.getPosition());\n    }\n\n    @Override\n    public int hashCode() {\n        return getPosition().hashCode();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/path/PathElementTile.java",
    "content": "package org.cyclops.integrateddynamics.capability.path;\n\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\n\n/**\n * Implementation of {@link IPathElement} for a tile entity.\n * @author rubensworks\n */\npublic class PathElementTile<T extends BlockEntity> extends PathElementCable {\n\n    private final T tile;\n    private final ICable cable;\n    private final DimPos position;\n\n    public PathElementTile(T tile, ICable cable) {\n        this.tile = tile;\n        this.cable = cable;\n        this.position = DimPos.of(tile.getLevel(), tile.getBlockPos());\n    }\n\n    protected T getTile() {\n        return tile;\n    }\n\n    @Override\n    protected ICable getCable() {\n        return cable;\n    }\n\n    @Override\n    public DimPos getPosition() {\n        return position;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/path/PathElementTileMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.capability.path;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport java.util.Set;\n\n/**\n * Implementation of {@link IPathElement} for {@link BlockEntityMultipartTicking}.\n * @author rubensworks\n */\npublic class PathElementTileMultipartTicking extends PathElementTile<BlockEntityMultipartTicking> {\n\n    public PathElementTileMultipartTicking(BlockEntityMultipartTicking tile, ICable cable) {\n        super(tile, cable);\n    }\n\n    @Override\n    public Set<ISidedPathElement> getReachableElements() {\n        // Add the reachable path elements from the parts that provide one.\n        Set<ISidedPathElement> pathElements = super.getReachableElements();\n        INetwork network = getTile().getNetwork();\n        IPartNetwork partNetwork = NetworkHelpers.getPartNetwork(network).orElse(null);\n        for (Direction side : Direction.values()) {\n            getTile().getPartContainer().getCapability(Capabilities.PathElement.PART, network, partNetwork, PartTarget.fromCenter(PartPos.of(getTile().getLevel(), getTile().getBlockPos(), side)))\n                    .ifPresent(pathElement -> pathElements.addAll(pathElement.getReachableElements()));\n        }\n        return pathElements;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/path/SidedPathElement.java",
    "content": "package org.cyclops.integrateddynamics.capability.path;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\nimport org.cyclops.integrateddynamics.api.path.SidedPathElementParams;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * @author rubensworks\n */\npublic class SidedPathElement implements ISidedPathElement {\n\n    private final IPathElement pathElement;\n    private final Direction side;\n\n    public SidedPathElement(IPathElement pathElement, @Nullable Direction side) {\n        this.pathElement = pathElement;\n        this.side = side;\n    }\n\n    @Override\n    public IPathElement getPathElement() {\n        return pathElement;\n    }\n\n    @Override\n    @Nullable\n    public Direction getSide() {\n        return side;\n    }\n\n    @Override\n    public SidedPathElementParams getParams() {\n        DimPos pos = getPathElement().getPosition();\n        return new SidedPathElementParams(pos.getLevel(), pos.getBlockPos(), Optional.ofNullable(getSide()));\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        return o instanceof ISidedPathElement && compareTo((ISidedPathElement)o) == 0;\n    }\n\n    @Override\n    public int compareTo(ISidedPathElement o) {\n        int pathElement = getPathElement().getPosition().compareTo(o.getPathElement().getPosition());\n        if (pathElement == 0) {\n            Direction thisSide = getSide();\n            Direction thatSide = o.getSide();\n            // If one of the sides is null, assume equality\n            return thisSide != null && thatSide != null ? thisSide.compareTo(thatSide) : 0;\n        }\n        return pathElement;\n    }\n\n    @Override\n    public int hashCode() {\n        return getPathElement().getPosition().hashCode();\n    }\n\n    @Override\n    public String toString() {\n        return \"[Sided PE: \" + getPathElement().getPosition() + \" @ \" + getSide() + \"]\";\n    }\n\n    public static SidedPathElement of(IPathElement pathElement, @Nullable Direction side) {\n        return new SidedPathElement(pathElement, side);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/valueinterface/ValueInterfaceDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.valueinterface;\n\nimport org.cyclops.integrateddynamics.api.evaluate.IValueInterface;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\n\nimport java.util.Optional;\n\n/**\n * Default implementation of {@link IValueInterface}.\n * @author rubensworks\n */\npublic class ValueInterfaceDefault implements IValueInterface {\n\n    private IValue value;\n\n    public ValueInterfaceDefault(IValue value) {\n        this.value = value;\n    }\n\n    @Override\n    public Optional<IValue> getValue() {\n        return Optional.of(value);\n    }\n\n    public void setValue(IValue value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/variablecontainer/VariableContainerDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.variablecontainer;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.IVariableContainer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\n\nimport java.util.Map;\n\n/**\n * Default implementation of {@link IVariableContainer}.\n * @author rubensworks\n */\npublic class VariableContainerDefault implements IVariableContainer {\n\n    private final Map<Integer, IVariableFacade> variableCache = Maps.newHashMap();\n\n    @Override\n    public Map<Integer, IVariableFacade> getVariableCache() {\n        return this.variableCache;\n    }\n\n    @Override\n    public void refreshVariables(INetwork network, Container inventory, boolean sendVariablesUpdateEvent, ValueDeseralizationContext valueDeseralizationContext){\n        // Invalidate variables\n        NetworkHelpers.getPartNetwork(network).ifPresent(partNetwork -> {\n            for (IVariableFacade variableFacade : getVariableCache().values()) {\n                IVariable<?> variable = variableFacade.getVariable(network, partNetwork);\n                if (variable != null) {\n                    variable.invalidate();\n                }\n            }\n        });\n\n        // Reset variable facades in inventory\n        getVariableCache().clear();\n        IVariableFacade firstInvalidVariableFacade = null;\n        for (int i = 0; i < inventory.getContainerSize(); i++) {\n            ItemStack itemStack = inventory.getItem(i);\n            if (!itemStack.isEmpty()) {\n                IVariableFacade variableFacade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(valueDeseralizationContext, itemStack);\n                if (variableFacade != null) {\n                    if (variableFacade.isValid()) {\n                        getVariableCache().put(variableFacade.getId(), variableFacade);\n                    } else if (firstInvalidVariableFacade == null) {\n                        firstInvalidVariableFacade = variableFacade;\n                    }\n                }\n            }\n        }\n\n        // If no valid variables were present, fallback to the first invalid variable facade.\n        // This is for example to make sure that empty variables are resolved to true.\n        if (getVariableCache().isEmpty() && firstInvalidVariableFacade != null) {\n            getVariableCache().put(firstInvalidVariableFacade.getId(), firstInvalidVariableFacade);\n        }\n\n        // Trigger event in network\n        if (sendVariablesUpdateEvent) {\n            if (network != null) {\n                network.getEventBus().post(new VariableContentsUpdatedEvent(network));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/capability/variablefacade/VariableFacadeHolderDefault.java",
    "content": "package org.cyclops.integrateddynamics.capability.variablefacade;\n\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHolder;\n\n/**\n * {@link IVariableFacadeHolder} that stores facade info in the root of the item's NBT tag.\n * @author rubensworks\n */\npublic class VariableFacadeHolderDefault implements IVariableFacadeHolder {\n\n    private final ItemStack itemStack;\n\n    public VariableFacadeHolderDefault(ItemStack itemStack) {\n        this.itemStack = itemStack;\n    }\n\n    @Override\n    public IVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext) {\n        return IntegratedDynamics._instance.getRegistryManager().\n                getRegistry(IVariableFacadeHandlerRegistry.class).handle(valueDeseralizationContext, itemStack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenCoalGenerator.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityCoalGenerator;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerCoalGenerator;\n\n/**\n * Gui for the coal generator.\n * @author rubensworks\n */\npublic class ContainerScreenCoalGenerator extends ContainerScreenExtended<ContainerCoalGenerator> {\n\n    public ContainerScreenCoalGenerator(ContainerCoalGenerator container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/coal_generator.png\");\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        int lastProgress = getMenu().getProgress();\n        if (lastProgress >= 0) {\n            guiGraphics.blit(RenderPipelines.GUI_TEXTURED, getGuiTexture(), getGuiLeftTotal() + 81, getGuiTopTotal() + 30 + lastProgress, 176,\n                    lastProgress, 14, BlockEntityCoalGenerator.MAX_PROGRESS - lastProgress + 1, 256, 256);\n        }\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int x, int y) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, x, y);\n        guiGraphics.text(font, this.title, this.titleLabelX, this.titleLabelY, ARGB.opaque(4210752), false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenDelay.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetNumberField;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.block.BlockDelayConfig;\nimport org.cyclops.integrateddynamics.core.client.gui.ContainerScreenActiveVariableBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerDelay;\n\n/**\n * Gui for the delay.\n * @author rubensworks\n */\npublic class ContainerScreenDelay extends ContainerScreenActiveVariableBase<ContainerDelay> {\n\n    private static final int ERROR_X = 110;\n    private static final int ERROR_Y = 26;\n\n    private WidgetNumberField numberFieldUpdateInterval = null;\n    private WidgetNumberField numberFieldCapacity = null;\n\n    public ContainerScreenDelay(ContainerDelay container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/delay.png\");\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 227;\n    }\n\n    @Override\n    protected int getErrorX() {\n        return ERROR_X;\n    }\n\n    @Override\n    protected int getErrorY() {\n        return ERROR_Y;\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        numberFieldUpdateInterval = new WidgetNumberField(font, leftPos + 98, topPos + 102, 73, 14, true,\n                Component.translatable(\"gui.integrateddynamics.partsettings.update_interval\"), true);\n        numberFieldUpdateInterval.setPositiveOnly(true);\n        numberFieldUpdateInterval.setMaxLength(64);\n        numberFieldUpdateInterval.setMaxLength(15);\n        numberFieldUpdateInterval.setVisible(true);\n        numberFieldUpdateInterval.setTextColor(ARGB.opaque(16777215));\n        numberFieldUpdateInterval.setCanLoseFocus(true);\n        addWidget(numberFieldUpdateInterval);\n\n        numberFieldCapacity = new WidgetNumberField(font, leftPos + 98, topPos + 126, 73, 14, true,\n                Component.translatable(\"gui.integrateddynamics.delay.capacity\"), true);\n        numberFieldCapacity.setMinValue(1);\n        numberFieldCapacity.setMaxValue(BlockDelayConfig.maxHistoryCapacity);\n        numberFieldCapacity.setMaxLength(64);\n        numberFieldCapacity.setMaxLength(15);\n        numberFieldCapacity.setVisible(true);\n        numberFieldCapacity.setTextColor(ARGB.opaque(16777215));\n        numberFieldCapacity.setCanLoseFocus(true);\n        addWidget(numberFieldCapacity);\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (!this.numberFieldUpdateInterval.charTyped(evt)\n                && !this.numberFieldCapacity.charTyped(evt)) {\n            return super.charTyped(evt);\n        } else {\n            onValueChanged();\n        }\n\n        return true;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (!this.numberFieldUpdateInterval.keyPressed(evt)\n                && !this.numberFieldCapacity.keyPressed(evt)) {\n            return super.keyPressed(evt);\n        } else {\n            onValueChanged();\n        }\n\n        return true;\n    }\n\n    protected void onValueChanged() {\n        int updateInterval = 1;\n        int capacity = 5;\n        try {\n            updateInterval = numberFieldUpdateInterval.getInt();\n        } catch (NumberFormatException e) {}\n        try {\n            capacity = numberFieldCapacity.getInt();\n        } catch (NumberFormatException e) {}\n        ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastUpdateValueId(), updateInterval);\n        ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastCapacityValueId(), capacity);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        boolean clicked = false;\n        if (this.numberFieldUpdateInterval.mouseClicked(evt, isDoubleClick)) {\n            onValueChanged();\n            clicked = true;\n        }\n        if (this.numberFieldCapacity.mouseClicked(evt, isDoubleClick)) {\n            onValueChanged();\n            clicked = true;\n        }\n        return clicked || super.mouseClicked(evt, isDoubleClick);\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        numberFieldUpdateInterval.extractRenderState(guiGraphics, mouseX - leftPos, mouseY - topPos, partialTicks);\n        numberFieldCapacity.extractRenderState(guiGraphics, mouseX - leftPos, mouseY - topPos, partialTicks);\n        // MCP: drawString\n        guiGraphics.text(font, Component.translatable(\"gui.integrateddynamics.partsettings.update_interval\"), leftPos + 8, topPos + 104, IModHelpers.get().getBaseHelpers().RGBAToInt(0, 0, 0, 255), false);\n        guiGraphics.text(font, Component.translatable(\"gui.integrateddynamics.delay.capacity\"), leftPos + 8, topPos + 128, IModHelpers.get().getBaseHelpers().RGBAToInt(0, 0, 0, 255), false);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, mouseX, mouseY);\n        guiGraphics.text(font, this.title, this.titleLabelX, this.titleLabelY, ARGB.opaque(4210752), false);\n        displayErrors.drawForeground(guiGraphics, getMenu().getReadErrors(), getErrorX(), getErrorY(), mouseX, mouseY, this, this.leftPos, this.topPos);\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        if (valueId == getMenu().getLastUpdateValueId()) {\n            numberFieldUpdateInterval.setValue(Integer.toString(getMenu().getLastUpdateValue()));\n        }\n        if (valueId == ((ContainerDelay) getMenu()).getLastCapacityValueId()) {\n            numberFieldCapacity.setValue(Integer.toString(((ContainerDelay) getMenu()).getLastCapacityValue()));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenLabeller.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.StringUtils;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLabeller;\nimport org.cyclops.integrateddynamics.network.packet.ItemStackRenamePacket;\nimport org.lwjgl.glfw.GLFW;\n\n/**\n * Gui for the labeller.\n * @author rubensworks\n */\npublic class ContainerScreenLabeller extends ContainerScreenExtended<ContainerLabeller> {\n\n    private WidgetTextFieldExtended searchField;\n\n    public ContainerScreenLabeller(ContainerLabeller container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title);\n        SimpleInventory temporaryInputSlots = container.getTemporaryInputSlots();\n        temporaryInputSlots.addDirtyMarkListener(() -> {\n            ItemStack itemStack = temporaryInputSlots.getItem(0);\n            IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n            IVariableFacade variableFacade = registry.handle(ValueDeseralizationContext.of(container.getPlayerIInventory().player.level()), itemStack);\n            String label = LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getLabel(variableFacade.getId());\n            if(label == null && !itemStack.isEmpty() && itemStack.has(DataComponents.CUSTOM_NAME)) {\n                label = itemStack.getHoverName().getString();\n            }\n            if(label != null) {\n                this.setText(label);\n            }\n        });\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/labeller.png\");\n    }\n\n    @Override\n    public void init() {\n        super.init();\n        addRenderableWidget(new ButtonText(this.leftPos + 133,  this.topPos + 8,\n                Component.translatable(\"item.integrateddynamics.labeller.button.write\"),\n                Component.translatable(\"item.integrateddynamics.labeller.button.write\"), button -> {\n            ItemStack itemStack = getMenu().getItemStack();\n            IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n            IVariableFacade variableFacade = registry.handle(ValueDeseralizationContext.of(container.getPlayerIInventory().player.level()), itemStack);\n            if(variableFacade.isValid()) {\n                int variableId = variableFacade.getId();\n                String label = StringUtils.isBlank(searchField.getValue()) ? \"\" : searchField.getValue();\n                LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().put(variableId, label);\n            } else if(!itemStack.isEmpty()) {\n                String name = searchField.getValue();\n                IntegratedDynamics._instance.getPacketHandler().sendToServer(new ItemStackRenamePacket(name));\n                getMenu().setItemStackName(name);\n            }\n        }));\n\n        int searchWidth = 87;\n        int searchX = 36;\n        int searchY = 11;\n        this.searchField = new WidgetTextFieldExtended(font, this.leftPos + searchX, this.topPos + searchY, searchWidth, font.lineHeight, Component.translatable(\"gui.cyclopscore.search\"));\n        this.searchField.setMaxLength(64);\n        this.searchField.setBordered(false);\n        this.searchField.setVisible(true);\n        this.searchField.setFocused(true);\n        this.searchField.setTextColor(ARGB.opaque(16777215));\n        this.searchField.setCanLoseFocus(false);\n        this.searchField.setValue(\"\");\n        this.searchField.setX(this.leftPos + (searchX + searchWidth) - this.searchField.getWidth());\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 113;\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (!this.searchField.charTyped(evt)) {\n            return super.charTyped(evt);\n        }\n        return true;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (evt.input() != GLFW.GLFW_KEY_ESCAPE) {\n            this.searchField.keyPressed(evt);\n            return true;\n        }\n        return super.keyPressed(evt);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return this.searchField.mouseClicked(evt, isDoubleClick)\n                || super.mouseClicked(evt, isDoubleClick);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        this.searchField.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n    }\n\n    public void setText(String text) {\n        this.searchField.setValue(text);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenLogicProgrammer.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammer;\n\n/**\n * Gui for the {@link org.cyclops.integrateddynamics.block.BlockLogicProgrammer}.\n * @author rubensworks\n */\npublic class ContainerScreenLogicProgrammer extends ContainerScreenLogicProgrammerBase<ContainerLogicProgrammer> {\n\n    public ContainerScreenLogicProgrammer(ContainerLogicProgrammer container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenLogicProgrammerBase.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.platform.InputConstants;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenScrolling;\nimport org.cyclops.cyclopscore.client.gui.image.Images;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElement;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.core.client.gui.subgui.SubGuiHolder;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.SubGuiValueTypeInfoBase;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerActivateElementPacket;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerLabelPacket;\nimport org.cyclops.integrateddynamics.proxy.ClientProxy;\nimport org.lwjgl.glfw.GLFW;\n\nimport javax.annotation.Nullable;\nimport java.awt.*;\nimport java.util.List;\n\n/**\n * Base gui for the logic programmer.\n * @author rubensworks\n */\npublic class ContainerScreenLogicProgrammerBase<C extends ContainerLogicProgrammerBase>\n        extends ContainerScreenScrolling<C>\n        implements ContainerLogicProgrammerBase.ScreenCallbackHandler {\n\n    public static final int BOX_HEIGHT = 18;\n    private static final Rectangle ITEM_POSITION = new Rectangle(19, 18, 56, BOX_HEIGHT - 1);\n\n    protected final SubGuiHolder subGuiHolder = new SubGuiHolder();\n    private final boolean hasLabeller;\n    protected RenderPattern operatorConfigPattern = null;\n    protected SubGuiOperatorInfo operatorInfoPattern = null;\n    protected boolean firstInit = true;\n    protected int relativeStep = -1;\n    protected boolean swallowNextCharacter = false;\n    private ButtonText resetButton;\n\n    public ContainerScreenLogicProgrammerBase(C container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title);\n        container.setScreenCallbackHandler(this);\n        this.titleLabelX = 87;\n\n        this.hasLabeller = playerInventory.contains(new ItemStack(RegistryEntries.ITEM_LABELLER));\n    }\n\n    @Override\n    protected Rectangle getScrollRegion() {\n        return new Rectangle(this.leftPos + 19, this.topPos + 18, 57, 178);\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        // Button to deselect the active LP element\n        addRenderableWidget(this.resetButton = new ButtonText(this.leftPos + 87,  this.topPos + 4,\n                Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_RESET),\n                Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_RESET), button -> {\n            if (container.getActiveElement() != null) {\n                container.returnWriteItemToPlayer();\n                handleElementActivation(container.getActiveElement(), true);\n            }\n        }));\n        this.resetButton.setHeight(12);\n        resetButton.visible = false;\n\n        subGuiHolder.init(this.leftPos, this.topPos);\n        if (firstInit) {\n            setSearchFieldFocussed(true);\n            firstInit = false;\n        }\n    }\n\n    @Override\n    public void containerTick() {\n        super.containerTick();\n        subGuiHolder.tick();\n    }\n\n    protected int getScrollX() {\n        return 5;\n    }\n\n    protected int getScrollY() {\n        return 18;\n    }\n\n    protected int getScrollHeight() {\n        return 178;\n    }\n\n    @Override\n    protected int getBaseXSize() {\n        return 256;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 240;\n    }\n\n    @Override\n    protected int getSearchX() {\n        return 6;\n    }\n\n    protected int getSearchWidth() {\n        return 70;\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/logic_programmer.png\");\n    }\n\n    protected float colorSmoothener(float color, boolean hover) {\n        return 1F - ((1F - color) / (hover ? 2F : 4F));\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        subGuiHolder.renderBg(guiGraphics, this.leftPos, this.topPos, getMinecraft().getTextureManager(), font, partialTicks, mouseX, mouseY);\n\n        // Draw container name\n        guiGraphics.text(font, Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_FILTER),\n                this.leftPos + offsetX + 5, this.topPos + offsetY + 208, IModHelpers.get().getBaseHelpers().RGBAToInt(80, 80, 80, 255),\n                false);\n\n        // Draw operators\n        ContainerLogicProgrammerBase container = (ContainerLogicProgrammerBase) getMenu();\n        int boxHeight = BOX_HEIGHT;\n        for(int i = 0; i < container.getPageSize(); i++) {\n            if(container.isElementVisible(i)) {\n                ILogicProgrammerElement element = container.getVisibleElement(i);\n\n                Triple<Float, Float, Float> rgb = IModHelpers.get().getBaseHelpers().intToRGB(element.getColor());\n                boolean hover = LogicProgrammerElementTypes.areEqual(container.getActiveElement(), element)\n                        || isPointInRegion(getElementPosition(container, i, false), new Point(mouseX, mouseY));\n                int color = ARGB.colorFromFloat(\n                        1F,\n                        colorSmoothener(rgb.getLeft(), hover),\n                        colorSmoothener(rgb.getMiddle(), hover),\n                        colorSmoothener(rgb.getRight(), hover)\n                );\n\n                // Background\n                guiGraphics.blit(RenderPipelines.GUI_TEXTURED, texture, leftPos + offsetX + ITEM_POSITION.x,\n                        topPos + offsetY + ITEM_POSITION.y + boxHeight * i, 19, 18, ITEM_POSITION.width, ITEM_POSITION.height, 256, 256, color);\n\n                // Arrow\n                if(hover) {\n                    guiGraphics.blit(RenderPipelines.GUI_TEXTURED, texture, leftPos + offsetX + ITEM_POSITION.x,\n                            topPos + offsetY + ITEM_POSITION.y + boxHeight * i, 0, 240, 3, 16, 256, 256, color);\n                }\n\n                // Operator info\n                String aspectName = element.getSymbol();\n                IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, aspectName,\n                        this.leftPos + offsetX + (hover ? 22 : 21),\n                        this.topPos + offsetY + 26 + boxHeight * i,\n                        53, IModHelpers.get().getBaseHelpers().RGBAToInt(40, 40, 40, 255), false, Font.DisplayMode.NORMAL);\n            }\n        }\n\n        // Draw arrow on write slot\n        guiGraphics.blit(RenderPipelines.GUI_TEXTURED, texture, leftPos + offsetX + ContainerLogicProgrammerBase.OUTPUT_X - 4,\n                topPos + offsetY + ContainerLogicProgrammerBase.OUTPUT_Y - 4, subGuiHolder.isEmpty() ? 7 : 3, 240, 4, 4, 256, 256);\n    }\n\n    protected Rectangle getElementPosition(ContainerLogicProgrammerBase container, int i, boolean absolute) {\n        return new Rectangle(ITEM_POSITION.x + offsetX + (absolute ? this.leftPos : 0),\n                ITEM_POSITION.y + BOX_HEIGHT * i + offsetY + (absolute ? this.topPos : 0),\n                ITEM_POSITION.width, ITEM_POSITION.height\n        );\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, mouseX, mouseY);\n        subGuiHolder.drawGuiContainerForegroundLayer(guiGraphics, this.leftPos, this.topPos, getMinecraft().getTextureManager(), font, mouseX, mouseY);\n\n        // Draw usage information\n        if (subGuiHolder.isEmpty()) {\n            // Create\n            Images.ARROW_LEFT.draw(guiGraphics, offsetX + 85, offsetY + 17);\n            guiGraphics.text(font, Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_INFO_CREATE),\n                    offsetX + 100, offsetY + 23, IModHelpers.get().getBaseHelpers().RGBAToInt(80, 80, 80, 255), false);\n\n            // Modify\n            Images.ARROW_DOWN.draw(guiGraphics, offsetX + 230, offsetY + 90);\n            MutableComponent modifyComponent = Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_INFO_MODIFY);\n            guiGraphics.text(font, modifyComponent,\n                    offsetX + 230 - font.width(modifyComponent), offsetY + 95, IModHelpers.get().getBaseHelpers().RGBAToInt(80, 80, 80, 255), false);\n\n            // Tooltip on write slot\n            if (this.isHovering(ContainerLogicProgrammerBase.OUTPUT_X, ContainerLogicProgrammerBase.OUTPUT_Y,\n                    ContainerScreenLogicProgrammerBase.BOX_HEIGHT, ContainerScreenLogicProgrammerBase.BOX_HEIGHT, mouseX, mouseY)\n                    && Minecraft.getInstance().player.containerMenu.getCarried().isEmpty()\n                    && !container.hasWriteItemInSlot()) {\n                this.drawTooltip(Lists.newArrayList(Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_TOOLTIP_WRITESLOT_MODIFY)), guiGraphics, mouseX, mouseY);\n            }\n        }\n\n        // Draw operator tooltips\n        ContainerLogicProgrammerBase container = getMenu();\n        for(int i = 0; i < container.getPageSize(); i++) {\n            if(container.isElementVisible(i)) {\n                ILogicProgrammerElement<?, ?, ?, ?> element = container.getVisibleElement(i);\n                if(isPointInRegion(getElementPosition(container, i, false), new Point(mouseX, mouseY))) {\n                    List<Component> lines = Lists.newLinkedList();\n                    element.loadTooltip(lines::add);\n                    drawTooltip(lines, guiGraphics, mouseX, mouseY);\n                }\n            }\n        }\n    }\n\n    protected void onActivateElement(ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?> element) {\n        this.resetButton.setFocused(false);\n        subGuiHolder.addSubGui(operatorInfoPattern = new SubGuiOperatorInfo(element));\n        operatorInfoPattern.init(leftPos, topPos);\n        subGuiHolder.addSubGui(operatorConfigPattern = element.getClient().createSubGui(ContainerLogicProgrammerBase.BASE_X, ContainerLogicProgrammerBase.BASE_Y, ContainerLogicProgrammerBase.MAX_WIDTH, ContainerLogicProgrammerBase.MAX_HEIGHT, this, (ContainerLogicProgrammerBase) getMenu()));\n        operatorConfigPattern.init(leftPos, topPos);\n    }\n\n    protected void onDeactivateElement(ILogicProgrammerElement element) {\n        subGuiHolder.clear();\n    }\n\n    @Override\n    public boolean handleElementActivation(ILogicProgrammerElement element, boolean sendToServer) {\n        boolean activate = false;\n        boolean deselect = false;\n        ContainerLogicProgrammerBase container = getMenu();\n        ILogicProgrammerElement newActive = null;\n        if (container.getActiveElement() == element) {\n            // Only allow deselection of the current LP element if the write slot is empty\n            if (!container.hasWriteItemInSlot()) {\n                deselect = true;\n                onDeactivateElement(element);\n            }\n        } else {\n            // Swap to another LP element\n            onDeactivateElement(element);\n            activate = true;\n            newActive = element;\n            if(element != null) {\n                onActivateElement(element);\n            }\n        }\n        resetButton.visible = newActive != null;\n        if (activate || deselect) {\n            container.setActiveElement(newActive,\n                    operatorConfigPattern == null ? 0 : operatorConfigPattern.getX(),\n                    operatorConfigPattern == null ? 0 : operatorConfigPattern.getY());\n        }\n        if (sendToServer) {\n            if (newActive != null) {\n                ILogicProgrammerElementType type = newActive.getType();\n                IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                        new LogicProgrammerActivateElementPacket(type.getUniqueName(), type.getName(newActive)));\n            } else if (deselect) {\n                IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                        new LogicProgrammerActivateElementPacket(Identifier.parse(\"\"), Identifier.parse(\"\")));\n            }\n        }\n        return activate;\n    }\n\n    @Nullable\n    @Override\n    public RenderPattern getOperatorConfigPattern() {\n        return operatorConfigPattern;\n    }\n\n    protected void setSearchFieldFocussed(boolean focused) {\n        getSearchField().setFocused(focused);\n    }\n\n    protected boolean isSearchFieldFocussed() {\n        return getSearchField().isFocused();\n    }\n\n    protected boolean selectPageElement(int elementId) {\n        ContainerLogicProgrammerBase container = getMenu();\n\n        // Deactivate current element\n        if (elementId < 0) {\n            handleElementActivation(container.getActiveElement(), true);\n            return false;\n        }\n\n        // Activate a new element\n        for(int i = 0; i < container.getPageSize(); i++) {\n            if (container.isElementVisible(i)) {\n                if (elementId-- == 0) {\n                    ILogicProgrammerElement element = container.getVisibleElement(i);\n                    if (container.getActiveElement() != element) {\n                        handleElementActivation(element, true);\n                    }\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    protected boolean handleKeyCode(KeyEvent evt) {\n        InputConstants.Key inputCode = InputConstants.getKey(evt);\n        if(evt.key() != GLFW.GLFW_KEY_LEFT_SHIFT && evt.key() != GLFW.GLFW_KEY_RIGHT_SHIFT) {\n            ContainerLogicProgrammerBase container = getMenu();\n            int pageSize = container.getPageSize();\n            int stepModifier = IModHelpers.get().getMinecraftClientHelpers().isShifted() ? pageSize - 1 : 1;\n            boolean isElementFocused = container.getActiveElement() != null && container.getActiveElement().getClient().isFocused(operatorConfigPattern);\n\n            if (ClientProxy.FOCUS_LP_SEARCH.isActiveAndMatches(inputCode)) {\n                // Focus search field\n                setSearchFieldFocussed(true);\n                swallowNextCharacter = true;\n                return true;\n            } else if (isElementFocused && ClientProxy.FOCUS_LP_RENAME.isActiveAndMatches(inputCode) && hasLabeller()) {\n                // Open labeller gui\n                operatorInfoPattern.onButtonEditClick();\n                swallowNextCharacter = true;\n                return true;\n            } else if (GLFW.GLFW_KEY_LEFT == evt.key() && (!isElementFocused && isSearchFieldFocussed())) {\n                // Unfocus search field\n                setSearchFieldFocussed(isSearchFieldFocussed());\n                return true;\n            } else if (!isElementFocused && GLFW.GLFW_KEY_DOWN == evt.key()) {\n                // Scroll down\n                if (!selectPageElement(relativeStep += stepModifier)) {\n                    relativeStep -= stepModifier;\n                    if (relativeStep > 0) {\n                        getScrollbar().scrollRelative(-stepModifier);\n                        selectPageElement(relativeStep);\n                    }\n                }\n                return true;\n            } else if (!isElementFocused && GLFW.GLFW_KEY_UP == evt.key()) {\n                // Scroll up\n                if (!(relativeStep >= 0 && selectPageElement(relativeStep -= stepModifier))) {\n                    getScrollbar().scrollRelative(stepModifier);\n                    selectPageElement(relativeStep = 0);\n                }\n                return true;\n            } else if (!isElementFocused\n                    && (GLFW.GLFW_KEY_RIGHT == evt.key() || GLFW.GLFW_KEY_TAB == evt.key()\n                    || GLFW.GLFW_KEY_ENTER == evt.key() || GLFW.GLFW_KEY_KP_ENTER == evt.key())) {\n                if (container.getActiveElement() != null) {\n                    container.getActiveElement().getClient().setFocused(operatorConfigPattern, true);\n                    setSearchFieldFocussed(false);\n                }\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (swallowNextCharacter) {\n            swallowNextCharacter = false;\n            return true;\n        }\n\n        return subGuiHolder.charTyped(evt) || handleKeyCode(new KeyEvent(evt.codepoint(), 0, 0)) || super.charTyped(evt);\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (evt.key() != GLFW.GLFW_KEY_ESCAPE) {\n            if (this.subGuiHolder.keyPressed(evt) || handleKeyCode(evt)) {\n                return true;\n            }\n        }\n        return super.keyPressed(evt);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        if (subGuiHolder.mouseClicked(evt, isDoubleClick)) {\n            if (isSearchFieldFocussed()) {\n                setSearchFieldFocussed(false);\n            }\n            return true;\n        }\n\n        ContainerLogicProgrammerBase container = getMenu();\n        for(int i = 0; i < container.getPageSize(); i++) {\n            if (container.isElementVisible(i)) {\n                ILogicProgrammerElement element = container.getVisibleElement(i);\n                if (isPointInRegion(getElementPosition(container, i, false), new Point((int) evt.x(), (int) evt.y()))) {\n                    boolean activated = handleElementActivation(element, true);\n                    relativeStep = activated ? i : -1;\n                    if (activated) {\n                        container.getActiveElement().getClient().setFocused(operatorConfigPattern, true);\n                        setSearchFieldFocussed(false);\n                        return true;\n                    }\n                }\n            }\n        }\n        boolean superRet = super.mouseClicked(evt, isDoubleClick);\n\n        // If the search box has been selected, de-active the current element.\n        if(isSearchFieldFocussed() &&\n                container.getActiveElement() != null && container.getActiveElement().getClient().isFocused(operatorConfigPattern)) {\n            container.getActiveElement().getClient().setFocused(operatorConfigPattern, false);\n            return true;\n        }\n\n        return superRet;\n    }\n\n    protected void label(String label) {\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(new LogicProgrammerLabelPacket(label));\n    }\n\n    protected boolean hasLabeller() {\n        return this.hasLabeller;\n    }\n\n    public class SubGuiOperatorInfo extends SubGuiValueTypeInfoBase<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase> {\n\n        private WidgetTextFieldExtended searchField;\n        private ButtonText button = null;\n\n        public SubGuiOperatorInfo(IGuiInputElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?> element) {\n            super(ContainerScreenLogicProgrammerBase.this, getMenu(), element, 88, 106, 139, 20);\n\n            if(hasLabeller()) {\n                buttonList.add(button = new ButtonText(0, 0, 6, 10, Component.translatable(\"gui.integrateddynamics.button.edit\"), Component.literal(\"E\"),\n                        (button) -> onButtonEditClick(), true));\n            }\n\n            int searchWidth = 113;\n            this.searchField = new WidgetTextFieldExtended(ContainerScreenLogicProgrammerBase.this.font, 0, 0, searchWidth, 11,\n                    Component.translatable(\"gui.cyclopscore.search\"));\n            this.searchField.setMaxLength(64);\n            this.searchField.setBordered(true);\n            this.searchField.setVisible(false);\n            this.searchField.setTextColor(ARGB.opaque(16777215));\n            this.searchField.setCanLoseFocus(true);\n            this.searchField.setValue(\"\");\n            this.searchField.setWidth(searchWidth);\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            int searchX = 90;\n            int searchY = 110;\n            this.searchField.setX(guiLeft + searchX);\n            this.searchField.setY(guiTop + searchY);\n\n            if (hasLabeller()) {\n                button.setX(guiLeft + 220);\n                button.setY(guiTop + 111);\n            }\n        }\n\n        @Override\n        protected boolean showError() {\n            return container.canWriteActiveElementPre();\n        }\n\n        @Override\n        protected Component getLastError() {\n            return container.getLastError();\n        }\n\n        @Override\n        protected Identifier getTexture() {\n            return texture;\n        }\n\n        @Override\n        public boolean charTyped(CharacterEvent evt) {\n            if (!this.searchField.isFocused() || !this.searchField.charTyped(evt)) {\n                return super.charTyped(evt);\n            } else {\n                label(this.searchField.getValue());\n                return true;\n            }\n        }\n\n        @Override\n        public boolean keyPressed(KeyEvent evt) {\n            if (this.searchField.isFocused() && evt.key() != GLFW.GLFW_KEY_ESCAPE) {\n                this.searchField.keyPressed(evt);\n                label(this.searchField.getValue());\n                return true;\n            }\n            return super.keyPressed(evt);\n        }\n\n        @Override\n        public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n            if(this.searchField.isVisible() && this.searchField.mouseClicked(evt, isDoubleClick)) {\n                return true;\n            }\n            return super.mouseClicked(evt, isDoubleClick);\n        }\n\n        @Override\n        public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font font, float partialTicks, int mouseX, int mouseY) {\n            super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, font, partialTicks, mouseX, mouseY);\n            this.searchField.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n\n        @Override\n        public boolean shouldRenderElementName() {\n            return !this.searchField.isVisible();\n        }\n\n        public void onButtonEditClick() {\n            this.searchField.setVisible(!this.searchField.isVisible());\n            if(this.searchField.isVisible()) {\n                this.searchField.setFocused(true);\n                label(this.searchField.getValue());\n            } else {\n                label(\"\");\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenLogicProgrammerPortable.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerPortable;\n\n/**\n * Gui for the {@link org.cyclops.integrateddynamics.item.ItemPortableLogicProgrammer}.\n * @author rubensworks\n */\npublic class ContainerScreenLogicProgrammerPortable extends ContainerScreenLogicProgrammerBase<ContainerLogicProgrammerPortable> {\n\n    public ContainerScreenLogicProgrammerPortable(ContainerLogicProgrammerPortable container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenMaterializer.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.client.gui.ContainerScreenActiveVariableBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerMaterializer;\n\n/**\n * Gui for the proxy.\n * @author rubensworks\n */\npublic class ContainerScreenMaterializer extends ContainerScreenActiveVariableBase<ContainerMaterializer> {\n\n    private static final int ERROR_X = 110;\n    private static final int ERROR_Y = 26;\n\n    public ContainerScreenMaterializer(ContainerMaterializer container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/materializer.png\");\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 189;\n    }\n\n    @Override\n    protected int getErrorX() {\n        return ERROR_X;\n    }\n\n    @Override\n    protected int getErrorY() {\n        return ERROR_Y;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.helper.IGuiHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.client.gui.ContainerScreenMechanicalMachine;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerMechanicalDryingBasin;\n\n/**\n * Gui for the mechanical squeezer.\n * @author rubensworks\n */\npublic class ContainerScreenMechanicalDryingBasin extends ContainerScreenMechanicalMachine<ContainerMechanicalDryingBasin> {\n\n    public ContainerScreenMechanicalDryingBasin(ContainerMechanicalDryingBasin container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/mechanical_drying_basin.png\");\n    }\n\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n\n        // Render progress\n        IModHelpers.get().getGuiHelpers().renderProgressBar(guiGraphics, getGuiTexture(), getGuiLeftTotal() + 84, getGuiTopTotal() + 31, 11, 28,\n                176, 120, IGuiHelpers.ProgressDirection.UP,\n                getMenu().getProgress(), getMenu().getMaxProgress());\n\n        // Render energy level\n        IModHelpers.get().getGuiHelpers().renderProgressBar(guiGraphics, getGuiTexture(), getGuiLeftTotal() + 8, getGuiTopTotal() + 16, 18, 60,\n                176, 60, IGuiHelpers.ProgressDirection.UP,\n                getMenu().getEnergy(), getMenu().getMaxEnergy());\n\n        // Render input fluid tank\n        IModHelpersNeoForge.get().getGuiHelpers().renderOverlayedFluidTank(guiGraphics, getMenu().getInputFluidStack(),\n                getMenu().getInputFluidCapacity(), getGuiLeftTotal() + 28, getGuiTopTotal() + 16,\n                18, 60, texture, 176, 0);\n\n        // Render output fluid tank\n        IModHelpersNeoForge.get().getGuiHelpers().renderOverlayedFluidTank(guiGraphics, getMenu().getOutputFluidStack(),\n                getMenu().getOutputFluidCapacity(), getGuiLeftTotal() + 150, getGuiTopTotal() + 16,\n                18, 60, texture, 176, 0);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        super.extractLabels(guiGraphics, mouseX, mouseY);\n\n        drawEnergyBarTooltip(guiGraphics, 8, 16, 18, 60, mouseX, mouseY);\n        drawFluidTankTooltip(guiGraphics, getMenu().getInputFluidStack(), getMenu().getInputFluidCapacity(), 28, 16, 18, 60, mouseX, mouseY);\n        drawFluidTankTooltip(guiGraphics, getMenu().getOutputFluidStack(), getMenu().getOutputFluidCapacity(), 150, 16, 18, 60, mouseX, mouseY);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonImage;\nimport org.cyclops.cyclopscore.client.gui.image.IImage;\nimport org.cyclops.cyclopscore.client.gui.image.Image;\nimport org.cyclops.cyclopscore.helper.IGuiHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.client.gui.ContainerScreenMechanicalMachine;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerMechanicalSqueezer;\n\n/**\n * Gui for the mechanical squeezer.\n * @author rubensworks\n */\npublic class ContainerScreenMechanicalSqueezer extends ContainerScreenMechanicalMachine<ContainerMechanicalSqueezer> {\n\n    private final IImage imageArrowDownEnabled;\n    private final IImage imageArrowDownDisabled;\n    private ButtonImage buttonToggleFluidEject;\n\n    public ContainerScreenMechanicalSqueezer(ContainerMechanicalSqueezer container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n        imageArrowDownEnabled = new Image(texture, 176, 138, 20, 10);\n        imageArrowDownDisabled = new Image(texture, 176, 148, 20, 10);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/mechanical_squeezer.png\");\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        addRenderableWidget(buttonToggleFluidEject = new ButtonImage(getGuiLeftTotal() + 149, getGuiTopTotal() + 71,\n                Component.translatable(\"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\"),\n                createServerPressable(ContainerMechanicalSqueezer.BUTTON_TOGGLE_FLUID_EJECT, (button) -> {}),imageArrowDownDisabled));\n    }\n\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n\n        // Update the image in the fluid eject toggle button\n        buttonToggleFluidEject.setImage(getMenu().isAutoEjectFluids()\n                ? imageArrowDownEnabled : imageArrowDownDisabled);\n\n        // Render progress\n        IModHelpers.get().getGuiHelpers().renderProgressBar(guiGraphics, getGuiTexture(), getGuiLeftTotal() + 73, getGuiTopTotal() + 36, 12, 18,\n                176, 120, IGuiHelpers.ProgressDirection.DOWN,\n                getMenu().getProgress(), getMenu().getMaxProgress());\n\n        // Render energy level\n        IModHelpers.get().getGuiHelpers().renderProgressBar(guiGraphics, getGuiTexture(), getGuiLeftTotal() + 8, getGuiTopTotal() + 16, 18, 60,\n                176, 60, IGuiHelpers.ProgressDirection.UP,\n                getMenu().getEnergy(), getMenu().getMaxEnergy());\n\n        // Render fluid tank\n        IModHelpersNeoForge.get().getGuiHelpers().renderOverlayedFluidTank(guiGraphics, getMenu().getFluidStack(),\n                getMenu().getFluidCapacity(), getGuiLeftTotal() + 150, getGuiTopTotal() + 10,\n                18, 60, texture, 176, 0);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        super.extractLabels(guiGraphics, mouseX, mouseY);\n\n        drawEnergyBarTooltip(guiGraphics, 8, 16, 18, 60, mouseX, mouseY);\n        drawFluidTankTooltip(guiGraphics, getMenu().getFluidStack(), getMenu().getFluidCapacity(), 150, 10, 18, 60, mouseX, mouseY);\n\n        // Draw fluid auto-eject toggle\n        IModHelpers.get().getGuiHelpers().renderTooltip(this, guiGraphics, 150, 70, 18, 10, mouseX, mouseY, () -> Lists.newArrayList(\n                Component.translatable(L10NValues.GUI_MECHANICAL_SQUEEZER_TOGGLEFLUIDAUTOEJECT,\n                        ChatFormatting.AQUA + IModHelpers.get().getL10NHelpers().localize(getMenu().isAutoEjectFluids() ?\n                                L10NValues.GENERAL_TRUE : L10NValues.GENERAL_FALSE)),\n                Component.translatable(L10NValues.GUI_MECHANICAL_SQUEEZER_TOGGLEFLUIDAUTOEJECT + \".info\")));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenOnTheDynamicsOfIntegration.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.resources.sounds.SimpleSoundInstance;\nimport net.minecraft.client.sounds.SoundManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.infobook.ScreenInfoBook;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.infobook.OnTheDynamicsOfIntegrationBook;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerOnTheDynamicsOfIntegration;\n\n/**\n * Gui for the On the Dynamics of Integration book.\n * @author rubensworks\n */\npublic class ContainerScreenOnTheDynamicsOfIntegration extends ScreenInfoBook<ContainerOnTheDynamicsOfIntegration> {\n\n    public ContainerScreenOnTheDynamicsOfIntegration(ContainerOnTheDynamicsOfIntegration container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title, OnTheDynamicsOfIntegrationBook.getInstance());\n    }\n\n    @Override\n    protected int getGuiWidth() {\n        return 283;\n    }\n\n    @Override\n    protected int getGuiHeight() {\n        return 180;\n    }\n\n    @Override\n    protected int getPageWidth() {\n        return 142;\n    }\n\n    @Override\n    protected int getPageYOffset() {\n        return 9;\n    }\n\n    @Override\n    protected int getFootnoteOffsetX() {\n        return -2;\n    }\n\n    @Override\n    protected int getFootnoteOffsetY() {\n        return -8;\n    }\n\n    @Override\n    protected int getPrevNextOffsetY() {\n        return 7;\n    }\n\n    @Override\n    protected int getPrevNextOffsetX() {\n        return 16;\n    }\n\n    @Override\n    protected int getOffsetXForPageBase(int page) {\n        return page == 0 ? 20 : 10;\n    }\n\n    @Override\n    public int getTitleColor() {\n        return IModHelpers.get().getBaseHelpers().RGBAToInt(70, 70, 150, 255);\n    }\n\n    @Override\n    public void playPageFlipSound(SoundManager soundHandler) {\n        soundHandler.play(SimpleSoundInstance.forUI(RegistryEntries.SOUNDEVENT_EFFECT_PAGE_FLIPSINGLE, 1.0F));\n    }\n\n    @Override\n    public void playPagesFlipSound(SoundManager soundHandler) {\n        soundHandler.play(SimpleSoundInstance.forUI(RegistryEntries.SOUNDEVENT_EFFECT_PAGE_FLIPMULTIPLE, 1.0F));\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID,\n                Reference.TEXTURE_PATH_GUI + \"on_the_dynamics_of_integration_gui.png\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenPartDisplay.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.neoforged.neoforge.client.settings.KeyConflictContext;\nimport net.neoforged.neoforge.client.settings.KeyModifier;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenMultipart;\nimport org.cyclops.integrateddynamics.core.part.panel.PartTypePanelVariableDriven;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerPartPanelVariableDriven;\nimport org.lwjgl.glfw.GLFW;\n\n\n/**\n * Gui for a writer part.\n * @author rubensworks\n */\npublic class ContainerScreenPartDisplay<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>>\n        extends ContainerScreenMultipart<P, S, ContainerPartPanelVariableDriven<P, S>> {\n\n    private static final int ERROR_X = 104;\n    private static final int ERROR_Y = 16;\n    private static final int OK_X = 104;\n    private static final int OK_Y = 16;\n\n    public ContainerScreenPartDisplay(ContainerPartPanelVariableDriven<P, S> container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        addRenderableWidget(new ButtonText(getGuiLeftTotal() + 128, getGuiTopTotal() + 32, 30, 12,\n                Component.translatable(\"gui.integrateddynamics.button.copy\"),\n                Component.translatable(\"gui.integrateddynamics.button.copy\"),\n                (button) -> valueToClipboard(), true));\n    }\n\n    @Override\n    protected String getNameId() {\n        return \"part_display\";\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n\n        Component readValue = getMenu().getReadValue();\n        int readValueColor = getMenu().getReadValueColor();\n        boolean ok = false;\n        if(readValue != null) {\n            ok = true;\n            IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, readValue.getString(),\n                    getGuiLeftTotal() + 53, getGuiTopTotal() + 38, 70, ARGB.opaque(readValueColor), false, Font.DisplayMode.NORMAL);\n        }\n\n        displayErrors.drawBackground(guiGraphics, getMenu().getReadErrors(), ERROR_X, ERROR_Y, OK_X, OK_Y, this,\n                this.leftPos, this.topPos, ok);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        super.extractLabels(guiGraphics, mouseX, mouseY);\n        // Render error tooltip\n        displayErrors.drawForeground(guiGraphics, getMenu().getReadErrors(), ERROR_X, ERROR_Y, mouseX, mouseY, this, this.leftPos, this.topPos);\n\n        // Draw tooltip over copy button\n        IModHelpers.get().getGuiHelpers().renderTooltip(this, guiGraphics, 128, 32, 30, 12, mouseX, mouseY,\n                () -> Lists.newArrayList(Component.translatable(\"gui.integrateddynamics.button.copy.info\")));\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (GLFW.GLFW_KEY_C == evt.codepoint() && KeyModifier.CONTROL.isActive(KeyConflictContext.GUI)) {\n            valueToClipboard();\n            return true;\n        }\n        return super.charTyped(evt);\n    }\n\n    @Override\n    protected int getBaseXSize() {\n        return 176;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 128;\n    }\n\n    protected void valueToClipboard() {\n        Component readValue = getMenu().getReadValue();\n        if (readValue != null) {\n            getMinecraft().keyboardHandler.setClipboard(readValue.getString());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenPartReader.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenMultipartAspects;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerPartReader;\n\nimport java.awt.*;\n\n/**\n * Gui for a reader part.\n * @author rubensworks\n */\npublic class ContainerScreenPartReader<P extends IPartTypeReader<P, S>, S extends IPartStateReader<P>>\n        extends ContainerScreenMultipartAspects<P, S, IAspectRead<?, ?>, ContainerPartReader<P, S>> {\n\n    public ContainerScreenPartReader(ContainerPartReader<P, S> container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected String getNameId() {\n        return \"part_reader\";\n    }\n\n    @Override\n    protected void drawAdditionalElementInfoForeground(GuiGraphicsExtractor guiGraphics, ContainerPartReader<P, S> container, int index, IAspectRead<?, ?> aspect, int mouseX, int mouseY) {\n\n    }\n\n    @Override\n    protected void drawAdditionalElementInfo(GuiGraphicsExtractor guiGraphics, ContainerPartReader<P, S> container, int index, IAspectRead<?, ?> aspect) {\n        // Get current aspect value\n        Pair<Component, Integer> readValues = container.getReadValue(aspect);\n        if(readValues != null && readValues.getLeft() != null) {\n            IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, readValues.getLeft().getString(), this.leftPos + offsetX + 16,\n                    this.topPos + offsetY + 39 + container.getAspectBoxHeight() * index,\n                    70, ARGB.opaque(readValues.getRight()), false, Font.DisplayMode.NORMAL);\n        }\n\n        // Render target item\n        // This could be cached if this would prove to be a bottleneck\n        ItemStack itemStack = container.writeAspectInfo(false, new ItemStack(RegistryEntries.ITEM_VARIABLE), container.getPlayerIInventory().player.level(), aspect);\n        Rectangle pos = getElementPosition(container, index, true);\n\n        guiGraphics.item(itemStack, pos.x, pos.y);\n    }\n\n    @Override\n    protected int getBaseXSize() {\n        return 195;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 213;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenPartWriter.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenMultipartAspects;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerPartWriter;\n\nimport java.awt.*;\n\n\n/**\n * Gui for a writer part.\n * @author rubensworks\n */\npublic class ContainerScreenPartWriter<P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>>\n        extends ContainerScreenMultipartAspects<P, S, IAspectWrite<?, ?>, ContainerPartWriter<P, S>> {\n\n    private static final int ERROR_X = 152;\n    private static final int ERROR_Y = 20;\n    private static final int OK_X = 152;\n    private static final int OK_Y = 20;\n\n    public ContainerScreenPartWriter(ContainerPartWriter<P, S> container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected String getNameId() {\n        return \"part_writer\";\n    }\n\n    @Override\n    protected void drawAdditionalElementInfoForeground(GuiGraphicsExtractor guiGraphics, ContainerPartWriter<P, S> container, int index, IAspectWrite<?, ?> aspect, int mouseX, int mouseY) {\n        // Render error tooltip\n        if(getMenu().isPartStateEnabled()) {\n            displayErrors.drawForeground(guiGraphics, getMenu().getAspectErrors(aspect), ERROR_X, ERROR_Y + container.getAspectBoxHeight() * index, mouseX, mouseY, this, this.leftPos, this.topPos);\n        }\n    }\n\n    @Override\n    protected void drawAdditionalElementInfo(GuiGraphicsExtractor guiGraphics, ContainerPartWriter<P, S> container, int index, IAspectWrite<?, ?> aspect) {\n        int aspectBoxHeight = container.getAspectBoxHeight();\n\n        // Render dummy target item\n        // This could be cached if this would prove to be a bottleneck\n        ItemStack itemStack = container.writeAspectInfo(false, new ItemStack(RegistryEntries.ITEM_VARIABLE), container.getPlayerIInventory().player.level(), aspect);\n        Rectangle pos = getElementPosition(container, index, true);\n        guiGraphics.item(itemStack, pos.x, pos.y);\n\n        // Render error symbol\n        if(getMenu().isPartStateEnabled()) {\n            displayErrors.drawBackground(guiGraphics, getMenu().getAspectErrors(aspect), ERROR_X, ERROR_Y + aspectBoxHeight * index, OK_X, OK_Y + aspectBoxHeight * index, this,\n                    this.leftPos, this.topPos, getMenu().getPartStateActiveAspect() == aspect);\n        }\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        ContainerPartWriter<?, ?> container = getMenu();\n        IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, container.getWriteValue().getString(), this.leftPos + offsetX + 53,\n                this.topPos + offsetY + 132, 70, ARGB.opaque(container.getWriteValueColor()), false, Font.DisplayMode.NORMAL);\n    }\n\n    @Override\n    protected int getBaseXSize() {\n        return 195;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 222;\n    }\n\n    @Override\n    public int getMaxLabelWidth() {\n        return 85;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenProxy.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.client.gui.ContainerScreenActiveVariableBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerProxy;\n\n/**\n * Gui for the proxy.\n * @author rubensworks\n */\npublic class ContainerScreenProxy extends ContainerScreenActiveVariableBase<ContainerProxy> {\n\n    private static final int ERROR_X = 110;\n    private static final int ERROR_Y = 26;\n\n    public ContainerScreenProxy(ContainerProxy container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/proxy.png\");\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 189;\n    }\n\n    @Override\n    protected int getErrorX() {\n        return ERROR_X;\n    }\n\n    @Override\n    protected int getErrorY() {\n        return ERROR_Y;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/container/ContainerScreenVariablestore.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.container;\n\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerVariablestore;\n\n/**\n * Gui for the variablestore\n * @author rubensworks\n */\npublic class ContainerScreenVariablestore extends ContainerScreenExtended<ContainerVariablestore> {\n\n    public ContainerScreenVariablestore(ContainerVariablestore container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.parse(\"textures/gui/container/generic_54.png\");\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return BlockEntityVariablestore.ROWS * 18 + 17 + 96;\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        guiGraphics.blit(RenderPipelines.GUI_TEXTURED, texture, leftPos + offsetX, topPos + offsetY, 0, 0, this.imageWidth, BlockEntityVariablestore.ROWS * 18 + 17, 256, 256);\n        guiGraphics.blit(RenderPipelines.GUI_TEXTURED, texture, leftPos + offsetX, topPos + offsetY + BlockEntityVariablestore.ROWS * 18 + 17, 0, 126, this.imageWidth, 96, 256, 256);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int x, int y) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, x, y);\n        guiGraphics.text(font, this.title, this.titleLabelX, this.titleLabelY, ARGB.opaque(4210752), false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/gui/image/Images.java",
    "content": "package org.cyclops.integrateddynamics.client.gui.image;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.client.gui.image.Image;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Default images provided by this mod.\n * @author rubensworks\n */\npublic class Images {\n\n    public static final Identifier ICONS = Identifier.fromNamespaceAndPath(IntegratedDynamics._instance.getModId(),\n            IntegratedDynamics._instance.getReferenceValue(ModBaseNeoForge.REFKEY_TEXTURE_PATH_GUI) + \"icons.png\");\n\n    public static final Image BUTTON_BACKGROUND_INACTIVE = new Image(ICONS, 0, 0, 18, 18);\n    public static final Image BUTTON_BACKGROUND_ACTIVE = new Image(ICONS, 18, 0, 18, 18);\n\n    public static final Image BUTTON_MIDDLE_OFFSET = new Image(ICONS, 0, 18, 18, 18);\n    public static final Image BUTTON_MIDDLE_SETTINGS = new Image(ICONS, 18, 18, 18, 18);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/model/CableModel.java",
    "content": "package org.cyclops.integrateddynamics.client.model;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.renderer.block.BlockStateModelSet;\nimport net.minecraft.client.resources.model.geometry.BakedQuad;\nimport net.minecraft.client.renderer.block.dispatch.BlockStateModel;\nimport net.minecraft.client.renderer.chunk.ChunkSectionLayer;\nimport net.minecraft.client.resources.model.ResolvedModel;\nimport net.minecraft.client.resources.model.UnbakedModel;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.entity.ItemOwner;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.client.renderer.block.BlockAndTintGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.model.data.ModelData;\nimport org.cyclops.cyclopscore.helper.ModelHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.block.BlockCable;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * A dynamic facadeModel for cables.\n * @author rubensworks\n */\npublic class CableModel extends CableModelBase {\n\n    public CableModel(BlockAndTintGetter level, BlockState state, Direction facing, RandomSource rand, ModelData modelData, ChunkSectionLayer renderType) {\n        super(level, state, facing, rand, modelData, renderType);\n    }\n\n    public CableModel(ItemStack itemStack, ClientLevel world, ItemOwner entity) {\n        super(itemStack, world, entity);\n    }\n\n    public CableModel() {\n        super();\n    }\n\n    @Override\n    protected boolean isRealCable(ModelData modelData) {\n        return ModelHelpers.getSafeProperty(modelData, BlockCable.REALCABLE, true);\n    }\n\n    @Override\n    protected Optional<BlockState> getFacade(ModelData modelData) {\n        return ModelHelpers.getSafeProperty(modelData, BlockCable.FACADE, Optional.empty());\n    }\n\n    @Override\n    protected boolean isConnected(ModelData modelData, Direction side) {\n        return ModelHelpers.getSafeProperty(modelData, BlockCable.CONNECTED[side.ordinal()], false);\n    }\n\n    @Override\n    protected boolean hasPart(ModelData modelData, Direction side) {\n        return getPartRenderPosition(modelData, side) != PartRenderPosition.NONE;\n    }\n\n    @Override\n    protected PartRenderPosition getPartRenderPosition(ModelData modelData, Direction side) {\n        return ModelHelpers.getSafeProperty(modelData,\n                BlockCable.PART_RENDERPOSITIONS[side.ordinal()], PartRenderPosition.NONE);\n    }\n\n    @Override\n    protected boolean shouldRenderParts(ModelData modelData) {\n        return ModelHelpers.getSafeProperty(modelData, BlockCable.PARTCONTAINER, null) != null;\n    }\n\n    @Override\n    protected BlockStateModel getPartModel(ModelData modelData, Direction side) {\n        IPartContainer partContainer = ModelHelpers.getSafeProperty(modelData, BlockCable.PARTCONTAINER, null);\n        BlockState blockState = partContainer != null && partContainer.hasPart(side) ? partContainer.getPart(side).getBlockState(partContainer, side) : null;\n        Minecraft mc = Minecraft.getInstance();\n        BlockStateModelSet blockModelSet = mc.getModelManager().getBlockStateModelSet();\n        return blockState != null ? blockModelSet.get(blockState) : blockModelSet.missingModel();\n    }\n\n    @Override\n    protected IRenderState getRenderState(ModelData modelData) {\n        return ModelHelpers.getSafeProperty(modelData, BlockCable.RENDERSTATE, null);\n    }\n\n    @Override\n    public List<BakedQuad> handleBlockState(BlockAndTintGetter level, BlockPos pos, BlockState state, Direction side, RandomSource rand, ModelData extraData, ChunkSectionLayer renderType) {\n        return new CableModel(level, state, side, rand, extraData, renderType).getGeneralQuads();\n    }\n\n    @Override\n    public List<BakedQuad> handleItemState(@Nullable ItemStack stack, @Nullable Level world, @Nullable ItemOwner entity) {\n        return new CableModel(stack, (ClientLevel) world, entity).getGeneralQuads();\n    }\n\n    @Override\n    public UnbakedModel wrapped() {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public @Nullable ResolvedModel parent() {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public String debugName() {\n        return Reference.MOD_ID + \":CableModel\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/model/CableModelBase.java",
    "content": "package org.cyclops.integrateddynamics.client.model;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Lists;\nimport com.mojang.math.Quadrant;\nimport net.minecraft.client.model.geom.builders.UVPair;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.renderer.block.BlockAndTintGetter;\nimport net.minecraft.client.renderer.block.dispatch.BlockModelRotation;\nimport net.minecraft.client.renderer.block.dispatch.BlockStateModel;\nimport net.minecraft.client.renderer.block.dispatch.BlockStateModelPart;\nimport net.minecraft.client.renderer.chunk.ChunkSectionLayer;\nimport net.minecraft.client.renderer.rendertype.RenderTypes;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.client.resources.model.ModelBaker;\nimport net.minecraft.client.renderer.block.dispatch.ModelState;\nimport net.minecraft.client.resources.model.cuboid.CuboidFace;\nimport net.minecraft.client.resources.model.cuboid.FaceBakery;\nimport net.minecraft.client.resources.model.cuboid.ItemTransform;\nimport net.minecraft.client.resources.model.cuboid.ItemTransforms;\nimport net.minecraft.client.resources.model.geometry.BakedQuad;\nimport net.minecraft.client.resources.model.sprite.Material;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.entity.ItemOwner;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.client.model.quad.BakedColors;\nimport net.neoforged.neoforge.client.model.quad.BakedNormals;\nimport net.neoforged.neoforge.model.data.ModelData;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.client.model.DelegatingDynamicItemAndBlockModel;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.ModelHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.block.BlockCableClientConfig;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.joml.Vector3f;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * A base dynamic facadeModel for cables.\n * @author rubensworks\n */\npublic abstract class CableModelBase extends DelegatingDynamicItemAndBlockModel {\n\n    public static ModelBaker MODEL_BAKER;\n    private static final Cache<Triple<IRenderState, Direction, ChunkSectionLayer>, List<BakedQuad>> CACHE_QUADS = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.MINUTES).build();\n\n    private static final int RADIUS = 4;\n    private static final int TEXTURE_SIZE = 16;\n\n    private static final int LENGTH_CONNECTION = (TEXTURE_SIZE - RADIUS) / 2;\n    private static final int LENGTH_CONNECTION_LIMITED = 1;\n    private static final int INV_LENGTH_CONNECTION = TEXTURE_SIZE - LENGTH_CONNECTION;\n    public static final float MIN = (float) LENGTH_CONNECTION / (float) TEXTURE_SIZE;\n    public static final float MAX = 1.0F - MIN;\n    private static final PartRenderPosition CABLE_RENDERPOSITION = new PartRenderPosition(-1,\n            (((float) TEXTURE_SIZE - (float) RADIUS) / 2 / (float) TEXTURE_SIZE),\n            (float) RADIUS / (float) TEXTURE_SIZE, (float) RADIUS / (float) TEXTURE_SIZE);\n\n    private final float[][][] quadVertexes = makeQuadVertexes(MIN, MAX, 1.00F);\n\n    protected static final ItemTransforms TRANSFORMS = ModelHelpers.modifyDefaultTransforms(ImmutableMap.of(\n            ItemDisplayContext.FIRST_PERSON_LEFT_HAND, new ItemTransform(\n                    new Vector3f(0, 45, 0),\n                    new Vector3f(0, 1f / 32, 0),\n                    new Vector3f(0.4F, 0.4F, 0.4F)),\n            ItemDisplayContext.FIRST_PERSON_RIGHT_HAND, new ItemTransform(\n                    new Vector3f(0, 225, 0),\n                    new Vector3f(0, 1f / 32, 0),\n                    new Vector3f(0.4F, 0.4F, 0.4F))\n    ));\n\n    public CableModelBase(BlockAndTintGetter level, BlockState blockState, Direction facing, RandomSource rand, ModelData modelData, ChunkSectionLayer renderType) {\n        super(level, blockState, facing, rand, modelData, renderType);\n    }\n\n    public CableModelBase(ItemStack itemStack, ClientLevel world, ItemOwner entity) {\n        super(itemStack, world, entity);\n    }\n\n    public CableModelBase() {\n        super();\n    }\n\n    protected static float[][][] makeQuadVertexes(float min, float max, float length) {\n        return new float[][][]{\n                {\n                        {min, length, min},\n                        {max, length, min},\n                        {max, max   , min},\n                        {min, max   , min},\n                },\n                {\n                        {min, max   , min},\n                        {min, max   , max},\n                        {min, length, max},\n                        {min, length, min},\n                },\n                {\n                        {min, max   , max},\n                        {max, max   , max},\n                        {max, length,  max},\n                        {min, length, max},\n                },\n                {\n                        {max, length, min},\n                        {max, length, max},\n                        {max, max   , max},\n                        {max, max   , min},\n                }\n        };\n    }\n\n    private Direction getSideFromVecs(Vector3f a, Vector3f b, Vector3f c) {\n        int dir = a.y == b.y && b.y == c.y ? 0 : (a.x == b.x && b.x == c.x ? 2 : 4);\n        if (dir == 0) {\n            dir += (c.y >= 0.5) ? 1 : 0;\n        } else if (dir == 2) {\n            dir += (c.x >= 0.5) ? 1 : 0;\n        } else if (dir == 4) {\n            dir += (c.z >= 0.5) ? 1 : 0;\n        }\n        return Direction.from3DDataValue(dir);\n    }\n\n    public List<BakedQuad> getFacadeQuads(BlockStateModel facadeModel, BlockState blockState, Direction side, PartRenderPosition partRenderPosition, ChunkSectionLayer renderType) {\n        List<BakedQuad> originalQuads = Lists.newArrayList();\n        List<BlockStateModelPart> parts = new ArrayList<>();\n        facadeModel.collectParts(rand, parts);\n        for (BlockStateModelPart collectPart : parts) {\n            originalQuads.addAll(collectPart.getQuads(null));\n            originalQuads.addAll(collectPart.getQuads(side));\n        }\n\n        return originalQuads.stream()\n                .flatMap(originalQuad -> {\n                    List<BakedQuad> ret = Lists.newLinkedList();\n                    if(partRenderPosition == PartRenderPosition.NONE) {\n                        addFacadeQuad(ret, originalQuad, 0, 0, 1f, 1f, side);\n                    } else {\n                        float w = partRenderPosition.getWidthFactorSide();\n                        float h = partRenderPosition.getHeightFactorSide();\n                        float u0 = 0f;\n                        float v0 = 0f;\n                        float u1 = (1f - w) / 2;\n                        float v1 = (1f - h) / 2;\n                        float u2 = u1 + w;\n                        float v2 = v1 + h;\n                        float u3 = 1f;\n                        float v3 = 1f;\n                        /*\n                         * We render the following eight boxes, excluding the part box in the middle.\n                         * -------\n                         * |1|2|3|\n                         * -------\n                         * |4|P|5|\n                         * -------\n                         * |6|7|8|\n                         * -------\n                         */\n                        addFacadeQuad(ret, originalQuad, u0, v0, u1, v1, side); // 1\n                        addFacadeQuad(ret, originalQuad, u1, v0, u2, v1, side); // 2\n                        addFacadeQuad(ret, originalQuad, u2, v0, u3, v1, side); // 3\n                        addFacadeQuad(ret, originalQuad, u0, v1, u1, v2, side); // 4\n                        addFacadeQuad(ret, originalQuad, u2, v1, u3, v2, side); // 5\n                        addFacadeQuad(ret, originalQuad, u0, v2, u1, v3, side); // 6\n                        addFacadeQuad(ret, originalQuad, u1, v2, u2, v3, side); // 7\n                        addFacadeQuad(ret, originalQuad, u2, v2, u3, v3, side); // 8\n                    }\n                    return ret.stream();\n                })\n                .collect(Collectors.toList());\n    }\n\n    private void addFacadeQuad(List<BakedQuad> quads, BakedQuad originalQuad, float u0, float v0, float u1, float v1, Direction side) {\n        Vector3f from = new Vector3f(u0 * 16f, v0 * 16f, 0f);\n        Vector3f to = new Vector3f(u1 * 16f, v1 * 16f, 0f);\n        TextureAtlasSprite texture = originalQuad.materialInfo().sprite();\n        CuboidFace.UVs blockFaceUV = new CuboidFace.UVs(16f - u1 * 16f, 16f - v1 * 16f, 16f - u0 * 16f, 16f - v0 * 16f);\n        Direction NO_FACE_CULLING = null;\n        String DUMMY_TEXTURE_NAME = \"\";\n        CuboidFace blockPartFace = new CuboidFace(NO_FACE_CULLING, originalQuad.materialInfo().tintIndex(), DUMMY_TEXTURE_NAME, blockFaceUV, Quadrant.R0);\n        ModelState transformation = getRotation(side);\n        boolean APPLY_SHADING = true;\n        quads.add(FaceBakery.bakeQuad(MODEL_BAKER, from, to, blockPartFace, new Material.Baked(texture, false), Direction.NORTH, transformation, null, APPLY_SHADING, 0));\n    }\n\n    public static BlockModelRotation getRotation(Direction facing) {\n        switch (facing) {\n            case DOWN:  return BlockModelRotation.get(Quadrant.fromXYAngles(Quadrant.R90, Quadrant.R180));\n            case UP:    return BlockModelRotation.get(Quadrant.fromXYAngles(Quadrant.R270, Quadrant.R180));\n            case NORTH: return BlockModelRotation.get(Quadrant.fromXYAngles(Quadrant.R0, Quadrant.R0));\n            case SOUTH: return BlockModelRotation.get(Quadrant.fromXYAngles(Quadrant.R0, Quadrant.R180));\n            case WEST:  return BlockModelRotation.get(Quadrant.fromXYAngles(Quadrant.R0, Quadrant.R270));\n            case EAST:  return BlockModelRotation.get(Quadrant.fromXYAngles(Quadrant.R0, Quadrant.R90));\n        }\n        throw new IllegalArgumentException(String.valueOf(facing));\n    }\n\n    protected abstract boolean isRealCable(ModelData modelData);\n    protected abstract Optional<BlockState> getFacade(ModelData modelData);\n    protected abstract boolean isConnected(ModelData modelData, Direction side);\n    protected abstract boolean hasPart(ModelData modelData, Direction side);\n    protected abstract PartRenderPosition getPartRenderPosition(ModelData modelData, Direction side);\n    protected abstract boolean shouldRenderParts(ModelData modelData);\n    protected abstract BlockStateModel getPartModel(ModelData modelData, Direction side);\n    protected abstract IRenderState getRenderState(ModelData modelData);\n\n    @Override\n    public int materialFlags() {\n        return 0;\n    }\n\n    @Override\n    public List<BakedQuad> getGeneralQuads() {\n        Triple<IRenderState, Direction, ChunkSectionLayer> cacheKey = null;\n        List<BakedQuad> cachedQuads = null;\n        if (GeneralConfig.cacheCableModels) {\n            IRenderState renderState = getRenderState(modelData);\n            if (renderState != null) {\n                cacheKey = Triple.of(renderState, this.facing, this.renderType);\n                cachedQuads = CACHE_QUADS.getIfPresent(cacheKey);\n            }\n        }\n        if (cachedQuads == null) {\n            List<BakedQuad> ret = Lists.newLinkedList();\n            TextureAtlasSprite texture = particleIcon();\n            Optional<BlockState> blockStateHolder = getFacade(modelData);\n            boolean renderCable = isItemStack() || (isRealCable(modelData) && (\n                    (!blockStateHolder.isPresent() && this.renderType == ChunkSectionLayer.SOLID)\n                            || (blockStateHolder.isPresent() && this.renderType == ChunkSectionLayer.TRANSLUCENT)));\n            for (Direction side : Direction.values()) {\n                boolean isConnected = isItemStack() ? side == Direction.EAST || side == Direction.WEST : isConnected(modelData, side);\n                boolean hasPart = !isItemStack() && hasPart(modelData, side);\n                if (hasPart && shouldRenderParts(modelData)) {\n                    try {\n                        List<BlockStateModelPart> parts = new ArrayList<>();\n                        getPartModel(modelData, side).collectParts(rand, parts);\n                        for (BlockStateModelPart collectPart : parts) {\n                            ret.addAll(collectPart.getQuads(null));\n                        }\n                    } catch (Exception e) {\n                        // Skip rendering this part, could occur when the player is still logging in.\n                    }\n                }\n                if (renderCable) {\n                    if (isConnected || hasPart) {\n                        int i = 0;\n                        float[][][] quadVertexes = this.quadVertexes;\n                        if (hasPart) {\n                            PartRenderPosition partRenderPosition = getPartRenderPosition(modelData, side);\n                            float depthFactor = partRenderPosition == PartRenderPosition.NONE ? 0F : partRenderPosition.getDepthFactor();\n                            quadVertexes = makeQuadVertexes(MIN, MAX, 1F - depthFactor);\n                        }\n                        for (float[][] v : quadVertexes) {\n                            Vector3f v1 = rotate(new Vector3f(v[0][0] - .5f, v[0][1] - .5f, v[0][2] - .5f), side).add(.5f, .5f, .5f);\n                            Vector3f v2 = rotate(new Vector3f(v[1][0] - .5f, v[1][1] - .5f, v[1][2] - .5f), side).add(.5f, .5f, .5f);\n                            Vector3f v3 = rotate(new Vector3f(v[2][0] - .5f, v[2][1] - .5f, v[2][2] - .5f), side).add(.5f, .5f, .5f);\n                            Vector3f v4 = rotate(new Vector3f(v[3][0] - .5f, v[3][1] - .5f, v[3][2] - .5f), side).add(.5f, .5f, .5f);\n                            Direction realSide = getSideFromVecs(v1, v2, v3);\n\n                            boolean invert = i == 2 || i == 1;\n                            int length = hasPart ? LENGTH_CONNECTION_LIMITED : LENGTH_CONNECTION;\n\n                            i++;\n                            ret.add(new BakedQuad(\n                                    v1,\n                                    v2,\n                                    v3,\n                                    v4,\n                                    UVPair.pack(texture.getU(LENGTH_CONNECTION / 16f), texture.getV(invert ? length / 16f : 0)),\n                                    UVPair.pack(texture.getU(INV_LENGTH_CONNECTION / 16f), texture.getV(invert ? length / 16f : 0)),\n                                    UVPair.pack(texture.getU(INV_LENGTH_CONNECTION / 16f), texture.getV(invert ? 0 : length / 16f)),\n                                    UVPair.pack(texture.getU(LENGTH_CONNECTION / 16f), texture.getV(invert ? 0 : length / 16f)),\n                                    realSide,\n                                    new BakedQuad.MaterialInfo(texture, ChunkSectionLayer.SOLID, RenderTypes.entityCutout(texture.atlasLocation()), -1, true, 0),\n                                    BakedNormals.UNSPECIFIED,\n                                    BakedColors.DEFAULT\n                            ));\n                        }\n                    } else {\n                        addBakedQuad(ret, MIN, MAX, MIN, MAX, MAX, texture, side);\n                    }\n                }\n            }\n\n            if (blockStateHolder.isPresent() && shouldRenderParts(modelData) && this.renderType != null && this.facing != null) {\n                BlockStateModel facadeModel = IModHelpers.get().getRenderHelpers().getBakedModel(blockStateHolder.get());\n                boolean isConnected = isItemStack() ? this.facing == Direction.EAST || this.facing == Direction.WEST : isConnected(modelData, this.facing);\n                PartRenderPosition partRenderPosition = PartRenderPosition.NONE;\n                boolean hasPart = !isItemStack() && hasPart(modelData, this.facing);\n                if (hasPart) partRenderPosition = getPartRenderPosition(modelData, this.facing);\n                else if (isConnected) partRenderPosition = CABLE_RENDERPOSITION;\n                ret.addAll(getFacadeQuads(facadeModel, blockStateHolder.get(), this.facing, partRenderPosition, this.renderType));\n            }\n\n            // Close the cable connections for items\n            if (isItemStack()) {\n                addBakedQuad(ret, MIN, MAX, MIN, MAX, 1, texture, Direction.EAST);\n                addBakedQuad(ret, MIN, MAX, MIN, MAX, 1, texture, Direction.WEST);\n            }\n            cachedQuads = ret;\n            if (cacheKey != null) {\n                CACHE_QUADS.put(cacheKey, cachedQuads);\n            }\n        }\n        return cachedQuads;\n    }\n\n    public TextureAtlasSprite particleIcon() {\n        return BlockCableClientConfig.BLOCK_TEXTURE;\n    }\n\n    @Override\n    public Material.Baked particleMaterial() {\n        return new Material.Baked(particleIcon(), false);\n    }\n\n    @Override\n    public ModelData getModelData(BlockAndTintGetter world, BlockPos pos, BlockState state, ModelData tileData) {\n        return IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityMultipartTicking.class)\n                .map(BlockEntityMultipartTicking::getConnectionState)\n                .orElse(ModelData.EMPTY);\n    }\n\n    @Override\n    public boolean usesBlockLight() {\n        return false; // If false, RenderHelper.setupGuiFlatDiffuseLighting() is called\n    }\n\n    @Override\n    public ItemTransforms getTopTransforms() {\n        return TRANSFORMS;\n    }\n\n    @Override\n    public List<ChunkSectionLayer> getRenderTypes(BlockState state, RandomSource rand, ModelData data) {\n        return List.of(ChunkSectionLayer.values());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/model/CableRenderState.java",
    "content": "package org.cyclops.integrateddynamics.client.model;\n\nimport net.minecraft.nbt.Tag;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\n/**\n * @author rubensworks\n */\npublic class CableRenderState implements IRenderState {\n\n    private final boolean realCable;\n    private final EnumFacingMap<Boolean> connected;\n    private final EnumFacingMap<PartHelpers.PartStateHolder<?, ?>> partData;\n    private final Tag facadeBlock;\n\n    public CableRenderState(boolean realCable, EnumFacingMap<Boolean> connected, EnumFacingMap<PartHelpers.PartStateHolder<?, ?>> partData, Tag facadeBlock) {\n        this.realCable = realCable;\n        this.connected = connected;\n        this.partData = partData;\n        this.facadeBlock = facadeBlock;\n    }\n\n    public boolean isRealCable() {\n        return realCable;\n    }\n\n    public EnumFacingMap<Boolean> getConnected() {\n        return connected;\n    }\n\n    public EnumFacingMap<PartHelpers.PartStateHolder<?, ?>> getPartData() {\n        return partData;\n    }\n\n    public Tag getFacadeBlock() {\n        return facadeBlock;\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        CableRenderState that = (CableRenderState) o;\n        if (realCable != that.realCable) return false;\n        if (!java.util.Objects.equals(connected, that.connected)) return false;\n        if (!java.util.Objects.equals(partData, that.partData)) return false;\n        return java.util.Objects.equals(facadeBlock, that.facadeBlock);\n    }\n\n    @Override\n    public int hashCode() {\n        return java.util.Objects.hash(realCable, connected, partData, facadeBlock);\n    }\n\n    @Override\n    public String toString() {\n        return \"CableRenderState(\" + \"realCable=\" + realCable + \", connected=\" + connected + \", partData=\" + partData + \", facadeBlock=\" + facadeBlock + \")\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/model/IRenderState.java",
    "content": "package org.cyclops.integrateddynamics.client.model;\n\n/**\n * @author rubensworks\n */\npublic interface IRenderState {\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/blockentity/ItemStackBlockEntityEnergyBatteryRender.java",
    "content": "package org.cyclops.integrateddynamics.client.render.blockentity;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;\nimport net.minecraft.client.renderer.special.SpecialModelRenderer;\nimport net.minecraft.client.renderer.state.level.CameraRenderState;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.phys.Vec3;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryBase;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.jetbrains.annotations.Nullable;\nimport org.joml.Vector3fc;\n\nimport java.util.function.Consumer;\n\n/**\n * @author rubensworks\n */\npublic class ItemStackBlockEntityEnergyBatteryRender implements SpecialModelRenderer<ItemStack> {\n\n    private final BlockEntityRenderDispatcher blockEntityRenderDispatcher;\n\n    public ItemStackBlockEntityEnergyBatteryRender() {\n        blockEntityRenderDispatcher = Minecraft.getInstance().getBlockEntityRenderDispatcher();\n    }\n\n    @Override\n    public @Nullable ItemStack extractArgument(ItemStack stack) {\n        return stack;\n    }\n\n    @Override\n    public void submit(@Nullable ItemStack itemStack, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, int lightCoords, int overlayCoords, boolean hasFoil, int outlineColor) {\n        if (itemStack == null || itemStack.isEmpty()) return;\n        BlockEntityEnergyBattery tile = new BlockEntityEnergyBattery(BlockPos.ZERO, RegistryEntries.BLOCK_ENERGY_BATTERY.get().defaultBlockState());\n        tile.setLevel(Minecraft.getInstance().level);\n        BlockEnergyBatteryBase.itemStackToTile(itemStack, tile);\n        BlockEntityRenderer<BlockEntityEnergyBattery, BlockEntityRenderState> renderer = this.blockEntityRenderDispatcher.getRenderer(tile);\n        BlockEntityRenderState renderState = renderer.createRenderState();\n        renderer.extractRenderState(tile, renderState, 0, Vec3.ZERO, null);\n        this.blockEntityRenderDispatcher.submit(renderState, poseStack, submitNodeCollector, new CameraRenderState());\n    }\n\n    @Override\n    public void getExtents(Consumer<Vector3fc> consumer) {\n\n    }\n\n    public static record Unbaked() implements SpecialModelRenderer.Unbaked<ItemStack> {\n        public static final MapCodec<ItemStackBlockEntityEnergyBatteryRender.Unbaked> MAP_CODEC = MapCodec.unit(ItemStackBlockEntityEnergyBatteryRender.Unbaked::new);\n\n        @Override\n        public MapCodec<ItemStackBlockEntityEnergyBatteryRender.Unbaked> type() {\n            return MAP_CODEC;\n        }\n\n        @Override\n        public SpecialModelRenderer<ItemStack> bake(BakingContext bakingContext) {\n            return new ItemStackBlockEntityEnergyBatteryRender();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/blockentity/RenderBlockEntityDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.client.render.blockentity;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.math.Axis;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;\nimport net.minecraft.client.renderer.feature.ModelFeatureRenderer;\nimport net.minecraft.client.renderer.item.ItemStackRenderState;\nimport net.minecraft.client.renderer.rendertype.RenderTypes;\nimport net.minecraft.client.renderer.state.level.CameraRenderState;\nimport net.minecraft.client.renderer.texture.OverlayTexture;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.phys.Vec3;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDryingBasin;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Renderer for the item inside the {@link org.cyclops.integrateddynamics.block.BlockDryingBasin}.\n *\n * @author rubensworks\n *\n */\npublic class RenderBlockEntityDryingBasin implements BlockEntityRenderer<BlockEntityDryingBasin, RenderBlockEntityDryingBasin.RenderState> {\n\n    public RenderBlockEntityDryingBasin(BlockEntityRendererProvider.Context context) {\n\n    }\n\n    @Override\n    public RenderState createRenderState() {\n        return new RenderState();\n    }\n\n    @Override\n    public void extractRenderState(BlockEntityDryingBasin blockEntity, RenderState renderState, float partialTick, Vec3 cameraPosition, @Nullable ModelFeatureRenderer.CrumblingOverlay breakProgress) {\n        BlockEntityRenderer.super.extractRenderState(blockEntity, renderState, partialTick, cameraPosition, breakProgress);\n        renderState.itemStack = blockEntity.getInventory().getItem(0);\n        renderState.fluidStack = blockEntity.getTank().getFluid();\n        renderState.rotation = blockEntity.getRandomRotation();\n        renderState.level = blockEntity.getLevel();\n    }\n\n    @Override\n    public void submit(RenderState renderState, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState cameraRenderState) {\n        if(!renderState.itemStack.isEmpty()) {\n            poseStack.pushPose();\n            poseStack.translate(-0.5F, -0.5F, -0.5F);\n            renderItem(poseStack, submitNodeCollector, renderState);\n            poseStack.popPose();\n        }\n\n        FluidStack fluid = renderState.fluidStack;\n        IModHelpersNeoForge.get().getRenderHelpers().renderFluidContext(fluid, poseStack, () -> {\n            float height = (float) ((fluid.getAmount() * 0.7D) / IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume() + 0.23D + 0.01D);\n            int brightness = Math.max(renderState.lightCoords, fluid.getFluid().getFluidType().getLightLevel(fluid));\n            int l2 = brightness >> 0x10 & 0xFFFF;\n            int i3 = brightness & 0xFFFF;\n\n            TextureAtlasSprite icon = IModHelpersNeoForge.get().getRenderHelpers().getFluidIcon(fluid, Direction.UP);\n            var fluidTintSource = Minecraft.getInstance().getModelManager().getFluidStateModelSet().get(fluid.getFluid().defaultFluidState()).fluidTintSource();\n            Triple<Float, Float, Float> color = IModHelpers.get().getBaseHelpers().intToRGB(fluidTintSource != null ? fluidTintSource.colorAsStack(fluid) : 0xFFFFFFFF);\n\n            submitNodeCollector.submitCustomGeometry(poseStack, RenderTypes.text(icon.atlasLocation()), (pose, vb) -> {\n                vb.addVertex(pose, 0.0625F, height, 0.0625F).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU0(), icon.getV1()).setUv2(l2, i3);\n                vb.addVertex(pose, 0.0625F, height, 0.9375F).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU0(), icon.getV0()).setUv2(l2, i3);\n                vb.addVertex(pose, 0.9375F, height, 0.9375F).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU1(), icon.getV0()).setUv2(l2, i3);\n                vb.addVertex(pose, 0.9375F, height, 0.0625F).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU1(), icon.getV1()).setUv2(l2, i3);\n            });\n        });\n    }\n\n    private void renderItem(PoseStack poseStack, SubmitNodeCollector submitNodeCollector, RenderState renderStateDryingBasin) {\n        ItemStackRenderState renderState = new ItemStackRenderState();\n        Minecraft.getInstance().getItemModelResolver().updateForTopItem(renderState, renderStateDryingBasin.itemStack, ItemDisplayContext.FIXED, renderStateDryingBasin.level, null, 0);\n\n        if (renderState.isOversizedInGui()) {\n            poseStack.translate(1F, 1.2F, 1F);\n            poseStack.scale(1.2F, 1.2F, 1.2F);\n        } else {\n            poseStack.translate(1F, 1.2F, 1F);\n            poseStack.mulPose(Axis.XP.rotationDegrees(25F));\n            poseStack.mulPose(Axis.YP.rotationDegrees(25F));\n            poseStack.mulPose(Axis.YP.rotationDegrees(renderStateDryingBasin.rotation));\n        }\n\n        renderState.submit(poseStack, submitNodeCollector, 15728880, OverlayTexture.NO_OVERLAY, 0);\n    }\n\n    public static class RenderState extends BlockEntityRenderState {\n        public ItemStack itemStack;\n        public FluidStack fluidStack;\n        public float rotation;\n        public Level level;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/blockentity/RenderBlockEntityEnergyBattery.java",
    "content": "package org.cyclops.integrateddynamics.client.render.blockentity;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.LevelRenderer;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;\nimport net.minecraft.client.renderer.feature.ModelFeatureRenderer;\nimport net.minecraft.client.renderer.rendertype.RenderTypes;\nimport net.minecraft.client.renderer.state.level.CameraRenderState;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.phys.Vec3;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBatteryConfigClient;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Renderer for rendering the energy overlay on the {@link org.cyclops.integrateddynamics.block.BlockEnergyBattery}.\n *\n * @author rubensworks\n *\n */\npublic class RenderBlockEntityEnergyBattery implements BlockEntityRenderer<BlockEntityEnergyBattery, RenderBlockEntityEnergyBattery.RenderState> {\n\n    private static final float OFFSET = 0.001F;\n    private static final float MINY = 0F;\n    private static final float MAXY = 1F;\n    private static final float MIN = 0F - OFFSET;\n    private static final float MAX = 1F + OFFSET;\n    private static float[][][] coordinates = {\n            { // DOWN\n                    {MIN, MINY, MIN},\n                    {MIN, MINY, MAX},\n                    {MAX, MINY, MAX},\n                    {MAX, MINY, MIN}\n            },\n            { // UP\n                    {MIN, MAXY, MIN},\n                    {MIN, MAXY, MAX},\n                    {MAX, MAXY, MAX},\n                    {MAX, MAXY, MIN}\n            },\n            { // NORTH\n                    {MIN, MINY, MIN},\n                    {MIN, MAXY, MIN},\n                    {MAX, MAXY, MIN},\n                    {MAX, MINY, MIN}\n            },\n            { // SOUTH\n                    {MAX, MINY, MAX},\n                    {MAX, MAXY, MAX},\n                    {MIN, MAXY, MAX},\n                    {MIN, MINY, MAX}\n            },\n            { // WEST\n                    {MIN, MINY, MAX},\n                    {MIN, MAXY, MAX},\n                    {MIN, MAXY, MIN},\n                    {MIN, MINY, MIN}\n            },\n            { // EAST\n                    {MAX, MINY, MIN},\n                    {MAX, MAXY, MIN},\n                    {MAX, MAXY, MAX},\n                    {MAX, MINY, MAX}\n            }\n    };\n\n    public RenderBlockEntityEnergyBattery(BlockEntityRendererProvider.Context context) {\n\n    }\n\n    @Override\n    public boolean shouldRender(BlockEntityEnergyBattery blockEntity, Vec3 cameraPos) {\n        return blockEntity.getBlockPos() == BlockPos.ZERO || BlockEntityRenderer.super.shouldRender(blockEntity, cameraPos);\n    }\n\n    @Override\n    public RenderState createRenderState() {\n        return new RenderState();\n    }\n\n    @Override\n    public void extractRenderState(BlockEntityEnergyBattery blockEntity, RenderState renderState, float partialTick, Vec3 cameraPosition, @Nullable ModelFeatureRenderer.CrumblingOverlay breakProgress) {\n        BlockEntityRenderer.super.extractRenderState(blockEntity, renderState, partialTick, cameraPosition, breakProgress);\n        renderState.energyStored = blockEntity.getEnergyStored();\n        renderState.maxEnergyStored = blockEntity.getMaxEnergyStored();\n        renderState.combinedLights = new EnumFacingMap<>();\n        for(Direction side : Direction.Plane.HORIZONTAL) {\n            renderState.combinedLights.put(side, LevelRenderer.getLightCoords(blockEntity.getLevel(), blockEntity.getBlockPos().offset(side.getUnitVec3i())));\n        }\n        renderState.creative = blockEntity.isCreative();\n        renderState.gameTime = blockEntity.getLevel().getGameTime();\n    }\n\n    @Override\n    public void submit(RenderState renderState, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState cameraRenderState) {\n        if(renderState.energyStored > 0) {\n            float heightBase = Math.min(1.0F, (float) renderState.energyStored / renderState.maxEnergyStored);\n            // Re-scale height to [0.125, 0.875] range as the energy bar does not take up 100% of the height.\n            float height = (heightBase * 12 / 16) + 0.125F;\n\n            poseStack.pushPose();\n\n            for(Direction side : Direction.Plane.HORIZONTAL) {\n                int combinedLight = renderState.combinedLights.get(side);\n                TextureAtlasSprite icon = BlockEntityEnergyBatteryConfigClient.ICON_OVERLAY;\n\n                float[][] c = coordinates[side.ordinal()];\n                float replacedMaxV = icon.getV1();\n                float replacedMinV = (icon.getV0() - icon.getV1()) * height + icon.getV1();\n\n                float r;\n                float g;\n                float b;\n                if (renderState.creative) {\n                    float tickFactor = (((float) renderState.gameTime % 20) / 10);\n                    if (tickFactor > 1) {\n                        tickFactor = -tickFactor + 1;\n                    }\n                    r = 0.8F + 0.2F * tickFactor;\n                    g = 0.42F;\n                    b = 0.60F + 0.40F * tickFactor;\n                } else {\n                    b = 1.0F;\n                    g = 1.0F;\n                    r = 1.0F;\n                }\n\n                submitNodeCollector.submitCustomGeometry(poseStack, RenderTypes.text(icon.atlasLocation()), (pose, vb) -> {\n                    vb.addVertex(pose, c[0][0], c[0][1] * height, c[0][2]).setColor(r, g, b, 1).setUv(icon.getU0(), replacedMaxV).setLight(combinedLight);\n                    vb.addVertex(pose, c[1][0], c[1][1] * height, c[1][2]).setColor(r, g, b, 1).setUv(icon.getU0(), replacedMinV).setLight(combinedLight);\n                    vb.addVertex(pose, c[2][0], c[2][1] * height, c[2][2]).setColor(r, g, b, 1).setUv(icon.getU1(), replacedMinV).setLight(combinedLight);\n                    vb.addVertex(pose, c[3][0], c[3][1] * height, c[3][2]).setColor(r, g, b, 1).setUv(icon.getU1(), replacedMaxV).setLight(combinedLight);\n                });\n            }\n\n            poseStack.popPose();\n        }\n    }\n\n    public static class RenderState extends BlockEntityRenderState {\n        public int energyStored;\n        public int maxEnergyStored;\n        public EnumFacingMap<Integer> combinedLights;\n        public boolean creative;\n        public long gameTime;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/blockentity/RenderBlockEntitySqueezer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.blockentity;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.LevelRenderer;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;\nimport net.minecraft.client.renderer.feature.ModelFeatureRenderer;\nimport net.minecraft.client.renderer.item.ItemStackRenderState;\nimport net.minecraft.client.renderer.rendertype.RenderTypes;\nimport net.minecraft.client.renderer.state.level.CameraRenderState;\nimport net.minecraft.client.renderer.texture.OverlayTexture;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.phys.Vec3;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.helper.DirectionHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntitySqueezer;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Renderer for the item inside the {@link org.cyclops.integrateddynamics.block.BlockDryingBasin}.\n *\n * @author rubensworks\n *\n */\npublic class RenderBlockEntitySqueezer implements BlockEntityRenderer<BlockEntitySqueezer, RenderBlockEntitySqueezer.RenderState> {\n\n    private static final float OFFSET = 0.01F;\n    private static final float MINY = 0.0625F;\n    private static final float MAXY = 0.125F - OFFSET;\n    private static final float MIN = 0F + OFFSET;\n    private static final float MAX = 1F - OFFSET;\n    private static float[][][] coordinates = {\n            { // DOWN\n                    {MIN, MINY, MIN},\n                    {MIN, MINY, MAX},\n                    {MAX, MINY, MAX},\n                    {MAX, MINY, MIN}\n            },\n            { // UP\n                    {MIN, MAXY, MIN},\n                    {MIN, MAXY, MAX},\n                    {MAX, MAXY, MAX},\n                    {MAX, MAXY, MIN}\n            },\n            { // NORTH\n                    {MIN, MINY, MIN},\n                    {MIN, MAXY, MIN},\n                    {MAX, MAXY, MIN},\n                    {MAX, MINY, MIN}\n            },\n            { // SOUTH\n                    {MIN, MINY, MAX},\n                    {MIN, MAXY, MAX},\n                    {MAX, MAXY, MAX},\n                    {MAX, MINY, MAX}\n            },\n            { // WEST\n                    {MIN, MINY, MIN},\n                    {MIN, MAXY, MIN},\n                    {MIN, MAXY, MAX},\n                    {MIN, MINY, MAX}\n            },\n            { // EAST\n                    {MAX, MINY, MIN},\n                    {MAX, MAXY, MIN},\n                    {MAX, MAXY, MAX},\n                    {MAX, MINY, MAX}\n            }\n    };\n\n    public RenderBlockEntitySqueezer(BlockEntityRendererProvider.Context context) {\n\n    }\n\n    @Override\n    public RenderState createRenderState() {\n        return new RenderState();\n    }\n\n    @Override\n    public void extractRenderState(BlockEntitySqueezer blockEntity, RenderState renderState, float partialTick, Vec3 cameraPosition, @Nullable ModelFeatureRenderer.CrumblingOverlay breakProgress) {\n        BlockEntityRenderer.super.extractRenderState(blockEntity, renderState, partialTick, cameraPosition, breakProgress);\n        renderState.itemStack = blockEntity.getInventory().getItem(0);\n        renderState.fluidStack = blockEntity.getTank().getFluid();\n        renderState.level = blockEntity.getLevel();\n        renderState.itemHeight = blockEntity.getItemHeight();\n    }\n\n    @Override\n    public void submit(RenderState renderState, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState cameraRenderState) {\n        if(!renderState.itemStack.isEmpty()) {\n            poseStack.pushPose();\n            poseStack.translate(-0.5F, -0.5F, -0.5F);\n            renderItem(poseStack, submitNodeCollector, renderState);\n            poseStack.popPose();\n        }\n\n        if(!renderState.fluidStack.isEmpty()) {\n            FluidStack fluid = renderState.fluidStack;\n            int combinedLightCorrected = LevelRenderer.getLightCoords(renderState.level, renderState.blockPos.offset(Direction.UP.getUnitVec3i()));\n            IModHelpersNeoForge.get().getRenderHelpers().renderFluidContext(fluid, poseStack, () -> {\n                float height = Math.max(0.0625F - OFFSET, fluid.getAmount() * 0.0625F / IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume() + 0.0625F - OFFSET);\n                int brightness = Math.max(combinedLightCorrected, fluid.getFluid().getFluidType().getLightLevel(fluid));\n                int l2 = brightness >> 0x10 & 0xFFFF;\n                int i3 = brightness & 0xFFFF;\n\n                for(Direction side : DirectionHelpers.DIRECTIONS) {\n                    TextureAtlasSprite icon = IModHelpersNeoForge.get().getRenderHelpers().getFluidIcon(fluid, Direction.UP);\n                    var fluidTintSource = Minecraft.getInstance().getModelManager().getFluidStateModelSet().get(fluid.getFluid().defaultFluidState()).fluidTintSource();\n                    Triple<Float, Float, Float> color = IModHelpers.get().getBaseHelpers().intToRGB(fluidTintSource != null ? fluidTintSource.colorAsStack(fluid) : 0xFFFFFFFF);\n\n                    submitNodeCollector.submitCustomGeometry(poseStack, RenderTypes.text(icon.atlasLocation()), (pose, vb) -> {\n                        float[][] c = coordinates[side.ordinal()];\n                        float replacedMaxV = (side == Direction.UP || side == Direction.DOWN) ?\n                                icon.getV1() : ((icon.getV1() - icon.getV0()) * height + icon.getV0());\n                        vb.addVertex(pose, c[0][0], getHeight(side, c[0][1], height), c[0][2]).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU0(), replacedMaxV).setUv2(l2, i3);\n                        vb.addVertex(pose, c[1][0], getHeight(side, c[1][1], height), c[1][2]).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU0(), icon.getV0()).setUv2(l2, i3);\n                        vb.addVertex(pose, c[2][0], getHeight(side, c[2][1], height), c[2][2]).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU1(), icon.getV0()).setUv2(l2, i3);\n                        vb.addVertex(pose, c[3][0], getHeight(side, c[3][1], height), c[3][2]).setColor(color.getLeft(), color.getMiddle(), color.getRight(), 1).setUv(icon.getU1(), replacedMaxV).setUv2(l2, i3);\n                    });\n                }\n            });\n        }\n    }\n\n    private void renderItem(PoseStack poseStack, SubmitNodeCollector submitNodeCollector, RenderState renderStateSqueezer) {\n        ItemStackRenderState renderState = new ItemStackRenderState();\n        Minecraft.getInstance().getItemModelResolver().updateForTopItem(renderState, renderStateSqueezer.itemStack, ItemDisplayContext.FIXED, renderStateSqueezer.level, null, 0);\n\n        poseStack.pushPose();\n        float yTop = (9 - renderStateSqueezer.itemHeight) * 0.125F;\n        poseStack.translate(1F, (yTop - 1F) / 2 + 1F, 1F);\n        if (renderState.isOversizedInGui()) {\n            poseStack.scale(1.7F, 1.7F, 1.7F);\n        }\n        poseStack.scale(1F, yTop - 0.125F, 1F);\n\n        renderState.submit(poseStack, submitNodeCollector, 15728880, OverlayTexture.NO_OVERLAY, 0);\n        poseStack.popPose();\n    }\n\n    private static float getHeight(Direction side, float height, float replaceHeight) {\n        if(height == MAXY) {\n            return replaceHeight;\n        }\n        return height;\n    }\n\n    public static class RenderState extends BlockEntityRenderState {\n        public ItemStack itemStack;\n        public FluidStack fluidStack;\n        public Level level;\n        public int itemHeight;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/blockentity/RenderCable.java",
    "content": "package org.cyclops.integrateddynamics.client.render.blockentity;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;\nimport net.minecraft.client.renderer.feature.ModelFeatureRenderer;\nimport net.minecraft.client.renderer.state.level.CameraRenderState;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.phys.Vec3;\nimport org.cyclops.integrateddynamics.api.client.render.part.IPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.capability.partcontainer.PartContainerTileMultipartTicking;\nimport org.cyclops.integrateddynamics.client.render.part.PartOverlayRenderers;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.Map;\n\n/**\n * Renderer for cable components.\n * @author rubensworks\n */\npublic class RenderCable implements BlockEntityRenderer<BlockEntityMultipartTicking, RenderCable.CableRenderState> {\n\n    private final BlockEntityRendererProvider.Context context;\n\n    public RenderCable(BlockEntityRendererProvider.Context context) {\n        this.context = context;\n    }\n\n    @Override\n    public RenderCable.CableRenderState createRenderState() {\n        return new RenderCable.CableRenderState();\n    }\n\n    @Override\n    public void extractRenderState(BlockEntityMultipartTicking blockEntity, RenderCable.CableRenderState renderState, float partialTick, Vec3 cameraPosition, @Nullable ModelFeatureRenderer.CrumblingOverlay breakProgress) {\n        BlockEntityRenderer.super.extractRenderState(blockEntity, renderState, partialTick, cameraPosition, breakProgress);\n        renderState.partContainer = blockEntity.getPartContainer();\n        renderState.parts = blockEntity.getPartContainer().getParts();\n        renderState.partialTicks = partialTick;\n    }\n\n    @Override\n    public void submit(RenderCable.CableRenderState renderState, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState cameraRenderState) {\n        for (Map.Entry<Direction, IPartType<?, ?>> entry : renderState.parts.entrySet()) {\n            // Draw part overlays\n            for (IPartOverlayRenderer renderer : PartOverlayRenderers.REGISTRY.getRenderers(entry.getValue())) {\n                renderer.submitPartOverlay(this.context, renderState.partContainer, entry.getKey(), entry.getValue(),\n                        renderState.partialTicks, poseStack, submitNodeCollector, renderState.lightCoords, 0);\n            }\n        }\n    }\n\n    public static class CableRenderState extends BlockEntityRenderState {\n        public PartContainerTileMultipartTicking partContainer;\n        public Map<Direction, IPartType<?, ?>> parts;\n        public float partialTicks;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/blockentity/SpecialModelRendererVariableOverlay.java",
    "content": "package org.cyclops.integrateddynamics.client.render.blockentity;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.special.SpecialModelRenderer;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.jetbrains.annotations.Nullable;\nimport org.joml.Vector3fc;\n\nimport java.util.function.Consumer;\n\n/**\n * @author rubensworks\n */\npublic class SpecialModelRendererVariableOverlay implements SpecialModelRenderer<Pair<Boolean, ItemStack>> {\n\n    @Override\n    public @Nullable Pair<Boolean, ItemStack> extractArgument(ItemStack stack) {\n        // Store shift state to force re-rendering when shift is held/unheld.\n        return Pair.of(IModHelpers.get().getMinecraftClientHelpers().isShifted(), stack);\n    }\n\n    @Override\n    public void submit(@Nullable Pair<Boolean, ItemStack> stateIn, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, int lightCoords, int overlayCoords, boolean hasFoil, int outlineColor) {\n        IVariableFacade variableFacade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(ValueDeseralizationContext.ofClient(), stateIn.getRight());\n        poseStack.translate(0.5F, 0.5F, 0.7F);\n        variableFacade.getClient().renderISTER(stateIn.getRight(), ItemDisplayContext.GUI, poseStack, submitNodeCollector, lightCoords, overlayCoords);\n    }\n\n    @Override\n    public void getExtents(Consumer<Vector3fc> consumer) {\n\n    }\n\n    public static record Unbaked() implements SpecialModelRenderer.Unbaked<Pair<Boolean, ItemStack>> {\n        public static final MapCodec<SpecialModelRendererVariableOverlay.Unbaked> MAP_CODEC = MapCodec.unit(SpecialModelRendererVariableOverlay.Unbaked::new);\n\n        @Override\n        public MapCodec<SpecialModelRendererVariableOverlay.Unbaked> type() {\n            return MAP_CODEC;\n        }\n\n        @Override\n        public SpecialModelRenderer<Pair<Boolean, ItemStack>> bake(BakingContext bakingContext) {\n            return new SpecialModelRendererVariableOverlay();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/level/PartOffsetsOverlayRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.level;\n\nimport com.google.common.collect.Lists;\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.RenderTypes;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.Vec3;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.client.event.RenderLevelStageEvent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\nimport org.cyclops.integrateddynamics.core.network.PartOffsetsClientNotifier;\nimport org.cyclops.integrateddynamics.network.packet.PartOffsetsSubscribePacket;\n\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * @author rubensworks\n */\npublic class PartOffsetsOverlayRenderer {\n\n    private static final PartOffsetsOverlayRenderer _INSTANCE = new PartOffsetsOverlayRenderer();\n\n    private boolean subscribedToServerChanges = false;\n\n    private List<PartOffsetsClientNotifier.Entry> data = Lists.newArrayList();\n\n    private PartOffsetsOverlayRenderer() {\n\n    }\n\n    public static PartOffsetsOverlayRenderer getInstance() {\n        return _INSTANCE;\n    }\n\n    private void subscribeToServerChanges() {\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(new PartOffsetsSubscribePacket(true));\n        this.subscribedToServerChanges = true;\n    }\n\n    private void unsubscribeToServerChanges() {\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(new PartOffsetsSubscribePacket(false));\n        this.subscribedToServerChanges = false;\n    }\n\n    public void clear() {\n        this.data.clear();\n    }\n\n    public void setData(List<PartOffsetsClientNotifier.Entry> data) {\n        this.data = data;\n    }\n\n    @SubscribeEvent\n    public void onRender(RenderLevelStageEvent.AfterTranslucentBlocks event) {\n        // If the player is holding a wrench, show the offsets of parts\n        // Only do this for parts with non-default target side or non-default offset\n        Player player = Minecraft.getInstance().player;\n        if (player.getItemInHand(InteractionHand.MAIN_HAND).is(WrenchHelpers.TAG_WRENCH)\n                || player.getItemInHand(InteractionHand.OFF_HAND).is(WrenchHelpers.TAG_WRENCH)) {\n            if (!subscribedToServerChanges) {\n                this.subscribeToServerChanges();\n            }\n\n            Vec3 eyePos = event.getLevelRenderState().cameraRenderState.pos;\n            for (PartOffsetsClientNotifier.Entry entry : this.data) {\n                this.renderOffset(event.getPoseStack(), Minecraft.getInstance().renderBuffers().bufferSource(), entry, eyePos);\n            }\n\n        } else if (subscribedToServerChanges) {\n            this.data.clear();\n            this.unsubscribeToServerChanges();\n        }\n    }\n\n    private void renderOffset(PoseStack matrixStack, MultiBufferSource renderTypeBuffer, PartOffsetsClientNotifier.Entry entry, Vec3 eyePos) {\n        double offsetX = eyePos.x;\n        double offsetY = eyePos.y;\n        double offsetZ = eyePos.z;\n\n        Random posRand = new Random(entry.source().asLong());\n        float r = 0.5F + posRand.nextFloat() / 2;\n        float g = 0.5F + posRand.nextFloat() / 2;\n        float b = 0.5F + posRand.nextFloat() / 2;\n        float a = 0.90F;\n\n        // Draw line from center to target\n        VertexConsumer vb = renderTypeBuffer.getBuffer(RenderTypes.SECONDARY_BLOCK_OUTLINE);\n        float minX = entry.source().getX() - (float) offsetX + 0.5F + entry.sourceSide().getStepX() * 0.5F;\n        float minY = entry.source().getY() - (float) offsetY + 0.5F + entry.sourceSide().getStepY() * 0.5F;\n        float minZ = entry.source().getZ() - (float) offsetZ + 0.5F + entry.sourceSide().getStepZ() * 0.5F;\n        float maxX = entry.source().getX() - (float) offsetX + 0.5F + entry.targetOffset().getX() + (entry.targetSide().getAxis() != entry.sourceSide().getAxis() ? entry.targetSide().getStepX() * 0.5F : 0);\n        float maxY = entry.source().getY() - (float) offsetY + 0.5F + entry.targetOffset().getY() + (entry.targetSide().getAxis() != entry.sourceSide().getAxis() ? entry.targetSide().getStepY() * 0.5F : 0);\n        float maxZ = entry.source().getZ() - (float) offsetZ + 0.5F + entry.targetOffset().getZ() + (entry.targetSide().getAxis() != entry.sourceSide().getAxis() ? entry.targetSide().getStepZ() * 0.5F : 0);\n        vb.addVertex(matrixStack.last().pose(), minX, minY, minZ).setLineWidth(2).setColor(r, g, b, a).setNormal(0.0F, 0.0F, 0.0F);\n        vb.addVertex(matrixStack.last().pose(), maxX, maxY, maxZ).setLineWidth(2).setColor(r, g, b, a).setNormal(0.0F, 0.0F, 0.0F);\n\n        // Draw target face\n        AABB bb = new AABB(entry.targetSide().getStepX() == 1 ? 0.9 : 0, entry.targetSide().getStepY() == 1 ? 0.9 : 0, entry.targetSide().getStepZ() == 1 ? 0.9 : 0,\n                entry.targetSide().getStepX() == -1 ? 0.1 : 1, entry.targetSide().getStepY() == -1 ? 0.1 : 1, entry.targetSide().getStepZ() == -1 ? 0.1 : 1);\n        bb = bb\n                .move(entry.source())\n                .move(entry.targetOffset().getX(), entry.targetOffset().getY(), entry.targetOffset().getZ())\n                .move(-offsetX, -offsetY, -offsetZ)\n                .inflate(0.05, 0.05, 0.05)\n                .inflate(-0.05, -0.05, -0.05);\n        IModHelpers.get().getRenderHelpers().renderLineBox(matrixStack, renderTypeBuffer.getBuffer(RenderTypes.lines()),\n                bb, r, g, b, a, 2.5f);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/model/FacadeModel.java",
    "content": "package org.cyclops.integrateddynamics.client.render.model;\n\nimport net.minecraft.client.color.item.Constant;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.renderer.block.dispatch.BlockStateModel;\nimport net.minecraft.client.renderer.block.dispatch.BlockStateModelPart;\nimport net.minecraft.client.renderer.item.*;\nimport net.minecraft.client.resources.model.geometry.BakedQuad;\nimport net.minecraft.client.resources.model.geometry.QuadCollection;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.entity.ItemOwner;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.joml.Matrix4f;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Dynamic facadeModel for facade items.\n * @author rubensworks\n */\npublic class FacadeModel implements ItemModel {\n\n    private final ItemModel emptyModel;\n    private final ModelRenderProperties modelrenderproperties;\n\n    public FacadeModel(ItemModel emptyModel, ModelRenderProperties modelrenderproperties) {\n        this.emptyModel = emptyModel;\n        this.modelrenderproperties = modelrenderproperties;\n    }\n\n    @Override\n    public void update(ItemStackRenderState renderState, ItemStack itemStack, ItemModelResolver itemModelResolver, ItemDisplayContext displayContext, @org.jetbrains.annotations.Nullable ClientLevel level, @org.jetbrains.annotations.Nullable ItemOwner entity, int seed) {\n        BlockState blockState = RegistryEntries.ITEM_FACADE.get().getFacadeBlock(itemStack);\n        if(blockState == null) {\n            emptyModel.update(renderState, itemStack, itemModelResolver, displayContext, level, entity, seed);\n        } else {\n            BlockStateModel bakedModel = IModHelpers.get().getRenderHelpers().getBakedModel(blockState);\n            List<BlockStateModelPart> parts = new ArrayList<>();\n            bakedModel.collectParts(RandomSource.create(seed), parts);\n            QuadCollection.Builder quadBuilder = new QuadCollection.Builder();\n            for (BlockStateModelPart collectPart : parts) {\n                for (Direction direction : Direction.values()) {\n                    for (BakedQuad quad : collectPart.getQuads(direction)) {\n                        quadBuilder.addCulledFace(direction, quad);\n                    }\n                }\n            }\n            new CuboidItemModelWrapper(List.of(new Constant(-1)), quadBuilder.build(), this.modelrenderproperties, new Matrix4f()).update(renderState, itemStack, itemModelResolver, displayContext, level, entity, seed);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/part/ConnectorOmniPartOverlayRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.part;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport it.unimi.dsi.fastutil.ints.Int2IntMap;\nimport it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.client.particle.ParticleBlurData;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.part.PartTypeConnectorOmniDirectional;\n\nimport java.util.Random;\n\n/**\n * Overlay renderer for the omni-directional connector for rendering particle effects.\n * @author rubensworks\n */\npublic class ConnectorOmniPartOverlayRenderer extends PartOverlayRendererBase {\n\n    private static final Int2IntMap CACHED_GROUP_COLORS = new Int2IntOpenHashMap();\n\n    private final Random rand = new Random();\n\n    protected static int getGroupColor(int group) {\n        if (!CACHED_GROUP_COLORS.containsKey(group)) {\n            Random rand = new Random(group);\n            int color = rand.nextInt(1 << 23) | (255 << 24);\n            CACHED_GROUP_COLORS.put(group, color);\n            return color;\n        }\n        return CACHED_GROUP_COLORS.get(group);\n    }\n\n    @Override\n    public void submitPartOverlay(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                                  Direction direction, IPartType partType, float partialTicks, PoseStack matrixStack,\n                                  SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay) {\n        BlockPos pos = partContainer.getPosition().getBlockPos();\n        if(!shouldRender(pos)) return;\n\n        if (rand.nextInt(20) == 0 && !Minecraft.getInstance().isPaused()) {\n            IPartState partStateUnsafe = partContainer.getPartState(direction);\n            if (partStateUnsafe instanceof PartTypeConnectorOmniDirectional.State) {\n                PartTypeConnectorOmniDirectional.State partState = (PartTypeConnectorOmniDirectional.State) partStateUnsafe;\n                if (partState.hasConnectorId()) {\n                    double tx = pos.getX() + 0.5F + direction.getStepX() * 1.15F - 0.03F + rand.nextFloat() * 0.04F\n                            + (direction.getAxis() != Direction.Axis.X ? 0.25F - rand.nextFloat() * 0.5F : 0F);\n                    double ty = pos.getY() + 0.5F + direction.getStepY() * 1.15F - 0.03F + rand.nextFloat() * 0.04F\n                            + (direction.getAxis() != Direction.Axis.Y ? 0.25F - rand.nextFloat() * 0.5F : 0F);\n                    double tz = pos.getZ() + 0.5F + direction.getStepZ() * 1.15F - 0.03F + rand.nextFloat() * 0.04F\n                            + (direction.getAxis() != Direction.Axis.Z ? 0.25F - rand.nextFloat() * 0.5F : 0F);\n\n                    float scale = 0.15F;\n                    Triple<Float, Float, Float> colors = IModHelpers.get().getBaseHelpers().intToRGB(getGroupColor(partState.getGroupId()));\n                    float red = colors.getLeft() + rand.nextFloat() * 0.1F - 0.05F;\n                    float green = colors.getMiddle() + rand.nextFloat() * 0.1F - 0.05F;\n                    float blue = colors.getRight() + rand.nextFloat() * 0.1F - 0.05F;\n                    float ageMultiplier = 17F;\n\n                    Minecraft.getInstance().level.addParticle(\n                            new ParticleBlurData(red, green, blue, scale, ageMultiplier),\n                            tx, ty, tz,\n                            -(direction.getStepX() * 0.05F + rand.nextFloat() * 0.02F - 0.01F),\n                            -(direction.getStepY() * 0.05F + rand.nextFloat() * 0.02F - 0.01F),\n                            -(direction.getStepZ() * 0.05F + rand.nextFloat() * 0.02F - 0.01F));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/part/DisplayPartOverlayRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.part;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.math.Axis;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.Entity;\nimport org.cyclops.cyclopscore.client.gui.image.Images;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.client.render.valuetype.ValueTypeWorldRenderers;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\n\n/**\n * Overlay renderer for the display part to display values on the part.\n * @author rubensworks\n */\npublic class DisplayPartOverlayRenderer extends PartOverlayRendererBase {\n\n    public static final float MAX = 12.5F;\n    protected static final float pixel = 0.0625F;  // 0.0625 == 1/16\n\n    @Override\n    protected void setMatrixOrientation(PoseStack matrixStack, Direction direction) {\n        super.setMatrixOrientation(matrixStack, direction);\n        float translateX = -1F - direction.getStepX() + 4 * pixel;\n        float translateY = 1F - direction.getStepY() - 4 * pixel;\n        float translateZ = direction.getStepZ() - pixel + 0.0025F;\n        if (direction == Direction.NORTH) {\n            translateZ += 1F;\n        } else if (direction == Direction.EAST) {\n            translateX += 1F;\n            translateZ += 1F;\n        } else if (direction == Direction.SOUTH) {\n            translateX += 1F;\n        } else if (direction == Direction.UP) {\n            translateX += 1F;\n            translateZ += 1F;\n        } else if (direction == Direction.DOWN) {\n            translateX += 1F;\n            translateY -= 1F;\n        }\n        matrixStack.translate(translateX, translateY, translateZ);\n    }\n\n    @Override\n    public void submitPartOverlay(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                                  Direction direction, IPartType partType, float partialTicks, PoseStack matrixStack,\n                                  SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay) {\n        // Set max light value, to make independent of surrounding blocks.\n        // Otherwise, this value could become zero when in a facade with a solid block on top. #1531\n        combinedLight = 15728640;\n\n        BlockPos pos = partContainer.getPosition().getBlockPos();\n        if(!shouldRender(pos)) return;\n\n        // Calculate the alpha to be used when the player is almost out of rendering bounds.\n        Entity renderEntity = Minecraft.getInstance().player;\n        float distanceFactor = (float) ((getMaxRenderDistance() - renderEntity.distanceToSqr(pos.getX(), pos.getY(), pos.getZ())) / 5);\n        float distanceAlpha = Math.min(1.0F, distanceFactor);\n        if(distanceAlpha < 0.05F) distanceAlpha = 0.05F; // Can't be 0 because the MC font renderer doesn't handle 0 alpha's properly.\n\n        matrixStack.pushPose();\n\n        float scale = 0.04F;\n        setMatrixOrientation(matrixStack, direction);\n        matrixStack.scale(scale, scale, scale);\n        matrixStack.scale(1, -1, 1);\n\n        IPartState partStateUnsafe = partContainer.getPartState(direction);\n        if(!(partStateUnsafe instanceof PartTypePanelDisplay.State)) {\n            drawError(context, matrixStack, submitNodeCollector, combinedLight, combinedOverlay, distanceAlpha);\n        } else {\n            PartTypePanelDisplay.State partState = (PartTypePanelDisplay.State) partStateUnsafe;\n            if (partState.getFacingRotation() == null) {\n                drawError(context, matrixStack, submitNodeCollector, combinedLight, combinedOverlay, distanceAlpha);\n                return;\n            }\n            int rotation = partState.getFacingRotation().ordinal() - 2;\n            matrixStack.translate(6, 6, 0);\n            matrixStack.mulPose(Axis.ZP.rotationDegrees(rotation * 90));\n            matrixStack.translate(-6, -6, 0);\n\n            IValue value = partState.getDisplayValue();\n            if (value != null && partState.isEnabled()) {\n                IValueType<?> valueType = value.getType();\n                IValueTypeWorldRenderer renderer = ValueTypeWorldRenderers.REGISTRY.getRenderer(valueType);\n                if (renderer == null) {\n                    renderer = ValueTypeWorldRenderers.DEFAULT;\n                }\n                renderer.submitValue(context, partContainer, direction, partType, value, partialTicks, matrixStack, submitNodeCollector, combinedLight, combinedOverlay, distanceAlpha);\n            } else if (!partState.getInventory().isEmpty()) {\n                drawError(context, matrixStack, submitNodeCollector, combinedLight, combinedOverlay, distanceAlpha);\n            }\n        }\n\n        matrixStack.popPose();\n    }\n\n    protected void drawError(BlockEntityRendererProvider.Context context, PoseStack matrixStack, SubmitNodeCollector submitNodeCollector,\n                             int combinedLight, int combinedOverlay, float distanceAlpha) {\n        Images.ERROR.drawWorldWithAlpha(Minecraft.getInstance().getTextureManager(), matrixStack, submitNodeCollector, combinedLight, combinedOverlay, 12.5F, 12.5F, distanceAlpha);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/part/PartOverlayRendererBase.java",
    "content": "package org.cyclops.integrateddynamics.client.render.part;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.math.Axis;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.Entity;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.client.render.part.IPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\n/**\n * Base class for part overlay renderers.\n * @author rubensworks\n */\npublic abstract class PartOverlayRendererBase implements IPartOverlayRenderer {\n\n    protected int getMaxRenderDistance() {\n        return (int) Math.pow(GeneralConfig.partOverlayRenderdistance, 2);\n    }\n\n    protected boolean shouldRender(BlockPos pos) {\n        if (!NetworkHelpers.shouldWork()) {\n            return false;\n        }\n        Entity renderEntity = Minecraft.getInstance().player;\n        return renderEntity.distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) < getMaxRenderDistance();\n    }\n\n    /**\n     * Sets the OpenGL matrix orientation for the given direction.\n     * @param matrixStack The matrix stack.\n     * @param direction The direction to orient the OpenGL matrix to.\n     */\n    protected void setMatrixOrientation(PoseStack matrixStack, Direction direction) {\n        short rotationY = 0;\n        short rotationX = 0;\n        if (direction == Direction.SOUTH) {\n            rotationY = 0;\n        } else if (direction == Direction.NORTH) {\n            rotationY = 180;\n        } else if (direction == Direction.EAST) {\n            rotationY = 90;\n        } else if (direction == Direction.WEST) {\n            rotationY = -90;\n        } else if (direction == Direction.UP) {\n            rotationX = -90;\n        } else if (direction == Direction.DOWN) {\n            rotationX = 90;\n        }\n        matrixStack.mulPose(Axis.YP.rotationDegrees(rotationY));\n        matrixStack.mulPose(Axis.XP.rotationDegrees(rotationX));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/part/PartOverlayRendererRegistry.java",
    "content": "package org.cyclops.integrateddynamics.client.render.part;\n\nimport com.google.common.base.Supplier;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Multimap;\nimport com.google.common.collect.Multimaps;\nimport com.google.common.collect.Sets;\nimport org.cyclops.integrateddynamics.api.client.render.part.IPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.api.client.render.part.IPartOverlayRendererRegistry;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\nimport java.util.Collection;\nimport java.util.Set;\n\n/**\n * Registry for {@link IPartOverlayRenderer}.\n * @author rubensworks\n */\npublic final class PartOverlayRendererRegistry implements IPartOverlayRendererRegistry {\n\n    private static PartOverlayRendererRegistry INSTANCE = new PartOverlayRendererRegistry();\n\n    private final Multimap<IPartType<?, ?>, IPartOverlayRenderer> renderers = Multimaps.newSetMultimap(Maps.<IPartType<?, ?>, Collection<IPartOverlayRenderer>>newIdentityHashMap(), new Supplier<Set<IPartOverlayRenderer>>() {\n        @Override\n        public Set<IPartOverlayRenderer> get() {\n            return Sets.newIdentityHashSet();\n        }\n    });\n\n    private PartOverlayRendererRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static PartOverlayRendererRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <R extends IPartOverlayRenderer> R register(IPartType<?, ?> partType, R renderer) {\n        renderers.put(partType, renderer);\n        return renderer;\n    }\n\n    @Override\n    public Collection<IPartOverlayRenderer> getRenderers(IPartType<?, ?> partType) {\n        return renderers.get(partType);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/part/PartOverlayRenderers.java",
    "content": "package org.cyclops.integrateddynamics.client.render.part;\n\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.render.part.IPartOverlayRendererRegistry;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\n\n/**\n * A collection of all part overlay renderers\n * @author rubensworks\n */\npublic class PartOverlayRenderers {\n\n    public static final IPartOverlayRendererRegistry REGISTRY = IntegratedDynamics._instance.getRegistryManager().getRegistry(IPartOverlayRendererRegistry.class);\n\n    public static final DisplayPartOverlayRenderer DISPLAY = REGISTRY.register(PartTypes.DISPLAY_PANEL, new DisplayPartOverlayRenderer());\n    public static final ConnectorOmniPartOverlayRenderer CONNECTOR_OMNIDIRECTIONAL = REGISTRY.register(PartTypes.CONNECTOR_OMNI, new ConnectorOmniPartOverlayRenderer());\n\n    public static void load() {}\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/BlockValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeBlock;\n\nimport java.util.Optional;\n\n/**\n * A value type world renderer for blocks.\n * @author rubensworks\n */\npublic class BlockValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Optional<BlockState> blockOptional = ((ValueObjectTypeBlock.ValueBlock) value).getRawValue();\n        if(blockOptional.isPresent()) {\n            // ItemStack\n            ItemStack itemStack = IModHelpers.get().getBlockHelpers().getItemStackFromBlockState(blockOptional.get());\n            if(!itemStack.isEmpty()) {\n                ItemValueTypeWorldRenderer.renderItemStack(matrixStack, nodeCollector, combinedLight, combinedOverlay, 0, itemStack, alpha);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/FluidValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.rendertype.RenderTypes;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeFluidStack;\n\n/**\n * A value type world renderer for fluids.\n * @author rubensworks\n */\npublic class FluidValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        FluidStack fluidStack = ((ValueObjectTypeFluidStack.ValueFluidStack) value).getRawValue();\n        if (!fluidStack.isEmpty()) {\n            int brightness = Math.max(combinedLight, fluidStack.getFluid().getFluidType().getLightLevel(fluidStack));\n            int l2 = brightness >> 0x10 & 0xFFFF;\n            int i3 = brightness & 0xFFFF;\n\n            // Fluid\n            matrixStack.pushPose();\n            TextureAtlasSprite icon = IModHelpersNeoForge.get().getRenderHelpers().getFluidIcon(fluidStack, Direction.UP);\n            var fluidTintSource = Minecraft.getInstance().getModelManager().getFluidStateModelSet().get(fluidStack.getFluid().defaultFluidState()).fluidTintSource();\n            Triple<Float, Float, Float> color = IModHelpers.get().getBaseHelpers().intToRGB(fluidTintSource != null ? fluidTintSource.colorAsStack(fluidStack) : 0xFFFFFFFF);\n\n            nodeCollector.submitCustomGeometry(matrixStack, RenderTypes.text(icon.atlasLocation()), (pose, vb) -> {\n                float min = 0F;\n                float max = 12.5F;\n                float u1 = icon.getU0();\n                float u2 = icon.getU1();\n                float v1 = icon.getV0();\n                float v2 = icon.getV1();\n                vb.addVertex(pose, max, max, 0).setColor(color.getLeft(), color.getMiddle(), color.getRight(), alpha).setUv(u2, v2).setUv2(l2, i3);\n                vb.addVertex(pose, max, min, 0).setColor(color.getLeft(), color.getMiddle(), color.getRight(), alpha).setUv(u2, v1).setUv2(l2, i3);\n                vb.addVertex(pose, min, min, 0).setColor(color.getLeft(), color.getMiddle(), color.getRight(), alpha).setUv(u1, v1).setUv2(l2, i3);\n                vb.addVertex(pose, min, max, 0).setColor(color.getLeft(), color.getMiddle(), color.getRight(), alpha).setUv(u1, v2).setUv2(l2, i3);\n\n                // Stack size\n                matrixStack.translate(7F, 8.5F, 0.1F);\n                String string = String.valueOf(fluidStack.getAmount());\n                float scale = ((float) 5) / (float) context.font().width(string);\n                matrixStack.scale(scale, scale, 1F);\n                nodeCollector.submitText(matrixStack, 0, 0, Component.literal(string).getVisualOrderText(),\n                        false, Font.DisplayMode.NORMAL, combinedLight, IModHelpers.get().getBaseHelpers().RGBAToInt(200, 200, 200, (int) (alpha * 255F)),\n                        0, 0);\n            });\n\n            matrixStack.popPose();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/IngredientsValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.google.common.base.Supplier;\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.client.render.part.DisplayPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * A value type world renderer for blocks.\n * @author rubensworks\n */\npublic class IngredientsValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Optional<IMixedIngredients> ingredientsOptional = ((ValueObjectTypeIngredients.ValueIngredients) value).getRawValue();\n        if(ingredientsOptional.isPresent()) {\n            IMixedIngredients ingredients = ingredientsOptional.get();\n\n            // Get a list of all values\n            List<IValue> values = Lists.newArrayList();\n            for (IngredientComponent<?, ?> component : ingredients.getComponents()) {\n                IIngredientComponentHandler componentHandler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n                for (Object instance : ingredients.getInstances(component)) {\n                    values.add(componentHandler.toValue(instance));\n                }\n            }\n\n            // Render ingredients in a square matrix\n            renderGrid(context, partContainer, direction, partType, values, partialTicks,\n                    matrixStack, nodeCollector, combinedLight, combinedOverlay, alpha);\n        }\n    }\n\n    public static void renderGrid(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                                  Direction direction, IPartType partType, List<IValue> values, float partialTicks,\n                                  PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                                  int combinedLight, int combinedOverlay, float alpha) {\n        matrixStack.pushPose();\n        int matrixRadius = getSmallestSquareFrom(values.size());\n        float scale = 1F / matrixRadius;\n        matrixStack.scale(scale, scale, 1);\n        for (int i = 0; i < matrixRadius; i++) {\n            for (int j = 0; j < matrixRadius; j++) {\n                int realIndex = i * matrixRadius + j;\n                if (realIndex >= values.size()) {\n                    break;\n                }\n                IValue renderValue = values.get(realIndex);\n                if (renderValue != null) {\n                    matrixStack.pushPose();\n                    matrixStack.translate(j * DisplayPartOverlayRenderer.MAX, i * DisplayPartOverlayRenderer.MAX, 0);\n\n                    // Call value renderer for each value\n                    IValueTypeWorldRenderer renderer = ValueTypeWorldRenderers.REGISTRY.getRenderer(renderValue.getType());\n                    if (renderer == null) {\n                        renderer = ValueTypeWorldRenderers.DEFAULT;\n                    }\n                    renderer.submitValue(context, partContainer, direction, partType, renderValue,\n                            partialTicks, matrixStack, nodeCollector, combinedLight, combinedOverlay, alpha);\n                    matrixStack.popPose();\n                }\n            }\n        }\n        matrixStack.popPose();\n    }\n\n    @Nullable\n    protected static <T> T prepareElementForTick(List<T> elements, int tick, Supplier<T> defaultFactory) {\n        return elements.size() > 0 ? elements.get(tick % elements.size()) : defaultFactory.get();\n    }\n\n    protected static int getSmallestSquareFrom(int n) {\n        for (;!isInt(Math.sqrt(n));n++);\n        return (int) Math.sqrt(n);\n    }\n\n    protected static final boolean isInt(double n) {\n        return n == Math.floor(n) && !Double.isInfinite(n);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/ItemValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.item.ItemStackRenderState;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack;\n\n/**\n * A value type world renderer for items.\n * @author rubensworks\n */\npublic class ItemValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        ItemStack itemStackOptional = ((ValueObjectTypeItemStack.ValueItemStack) value).getRawValue();\n        if(!itemStackOptional.isEmpty()) {\n            // ItemStack\n            renderItemStack(matrixStack, nodeCollector, combinedLight, combinedOverlay, 0, itemStackOptional, alpha);\n\n            // Stack size\n            matrixStack.pushPose();\n            matrixStack.translate(7F, 8.5F, 0.3F);\n            String stackSize = String.valueOf(itemStackOptional.getCount());\n            float scale = 1F / ((float) stackSize.length() + 1F);\n            matrixStack.scale(scale, scale, 1F);\n            nodeCollector.submitText(matrixStack, 0, 0, Component.literal(stackSize).getVisualOrderText(),\n                    false, Font.DisplayMode.NORMAL, combinedLight, IModHelpers.get().getBaseHelpers().RGBAToInt(200, 200, 200, (int) (alpha * 255F)), 0, 0);\n            matrixStack.popPose();\n        }\n    }\n\n    public static void renderItemStack(PoseStack matrixStack, SubmitNodeCollector nodeCollector, int combinedLight, int combinedOverlay, int outlineColor, ItemStack itemStack, float alpha) {\n        // ItemStack\n        matrixStack.pushPose();\n        matrixStack.translate(6.2, 6.2, 0.1F);\n        matrixStack.scale(16F, -16F, 16F);\n        matrixStack.scale(0.74F, 0.74F, 0.01F);\n\n        // Derived from ItemRenderer\n        ItemStackRenderState renderState = new ItemStackRenderState();\n        Minecraft.getInstance().getItemModelResolver().updateForTopItem(renderState, itemStack, ItemDisplayContext.GUI, null, null, 0);\n        renderState.submit(matrixStack, nodeCollector, combinedLight, combinedOverlay, outlineColor);\n\n        matrixStack.popPose();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/ListValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\n\nimport java.util.List;\n\n/**\n * A text-based value type world renderer for lists.\n * @author rubensworks\n */\npublic class ListValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    private static final float MAX = 12.5F;\n    private static final float MARGIN_FACTOR = 1.1F;\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Font fontRenderer = context.font();\n        float maxWidth = 0;\n\n        List<Pair<String, Integer>> lines = Lists.newLinkedList();\n        IValueType listType = ((ValueTypeList.ValueList<?, ?>) value).getRawValue().getValueType();\n        for(IValue element : ((ValueTypeList.ValueList<?, ?>) value).getRawValue()) {\n            if(lines.size() >= ValueTypeList.MAX_RENDER_LINES) {\n                lines.add(Pair.of(\"...\", listType.getDisplayColor()));\n                break;\n            } else {\n                IValueType elementType = element.getType();\n                String string = \" - \" + elementType.toCompactString(element).getString();\n                float width = fontRenderer.width(string) - 1;\n                lines.add(Pair.of(string, elementType.getDisplayColor()));\n                maxWidth = Math.max(maxWidth, width);\n            }\n        }\n\n        float singleHeight = fontRenderer.lineHeight;\n        float totalHeight = singleHeight * lines.size();\n\n        matrixStack.pushPose();\n\n        float scaleX = MAX / (maxWidth * MARGIN_FACTOR);\n        float scaleY = MAX / (totalHeight * MARGIN_FACTOR);\n        float scale = Math.min(scaleX, scaleY); // Maintain aspect ratio\n        float newWidth = maxWidth * scale;\n        float newHeight = totalHeight * scale;\n        matrixStack.translate((MAX - newWidth) / 2, (MAX - newHeight) / 2, 0F);\n        matrixStack.scale(scale, scale, 1F);\n\n        int offset = 0;\n        for(Pair<String, Integer> line : lines) {\n            int color = IModHelpers.get().getBaseHelpers().addAlphaToColor(line.getRight(), alpha);\n            nodeCollector.submitText(matrixStack, 0, offset, Component.literal(line.getLeft()).getVisualOrderText(),\n                    false, Font.DisplayMode.NORMAL, combinedLight, color,\n                    0, 0);\n            offset += singleHeight;\n        }\n\n        matrixStack.popPose();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/NbtValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.chat.Component;\nimport org.apache.commons.lang3.StringUtils;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeNbt;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.List;\n\n/**\n * A text-based value type world renderer for NBT tags.\n * @author rubensworks\n */\npublic class NbtValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    private static final int MAX_LINES = 30;\n    private static final float MAX = 12.5F;\n    private static final float MARGIN_FACTOR = 1.1F;\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Font fontRenderer = context.font();\n        Wrapper<Float> maxWidth = new Wrapper<>(0F);\n\n        List<String> lines = Lists.newLinkedList();\n        ((ValueTypeNbt.ValueNbt) value).getRawValue().ifPresent(tag -> {\n            if (tag instanceof CompoundTag) {\n                CompoundTag tagCompound = (CompoundTag) tag;\n                lines.add(\"{\");\n                for (String key : tagCompound.keySet()) {\n                    if (lines.size() >= MAX_LINES) {\n                        lines.add(\"...\");\n                        break;\n                    } else {\n                        Tag subTag = ValueTypes.NBT.filterBlacklistedTags(tagCompound.get(key));\n                        String string = \"  \" + key + \": \" + StringUtils.abbreviate(subTag.toString(), 40) + \"\";\n                        float width = fontRenderer.width(string) - 1;\n                        lines.add(string);\n                        maxWidth.set(Math.max(maxWidth.get(), width));\n                    }\n                }\n                lines.add(\"}\");\n            } else {\n                String string = tag.toString();\n                lines.add(string);\n                maxWidth.set((float) (fontRenderer.width(string) - 1));\n            }\n        });\n\n        float singleHeight = fontRenderer.lineHeight;\n        float totalHeight = singleHeight * lines.size();\n\n        matrixStack.pushPose();\n\n        float scaleX = MAX / (maxWidth.get() * MARGIN_FACTOR);\n        float scaleY = MAX / (totalHeight * MARGIN_FACTOR);\n        float scale = Math.min(scaleX, scaleY); // Maintain aspect ratio\n        float newWidth = maxWidth.get() * scale;\n        float newHeight = totalHeight * scale;\n        matrixStack.translate((MAX - newWidth) / 2, (MAX - newHeight) / 2, 0F);\n        matrixStack.scale(scale, scale, 1F);\n\n        int offset = 0;\n        for(String line : lines) {\n            int color = IModHelpers.get().getBaseHelpers().addAlphaToColor(ValueTypes.NBT.getDisplayColor(), alpha);\n            nodeCollector.submitText(matrixStack, 0, offset, Component.literal(line).getVisualOrderText(),\n                    false, Font.DisplayMode.NORMAL, combinedLight, color,\n                    0, 0);\n            offset += singleHeight;\n        }\n\n        matrixStack.popPose();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/OperatorValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.List;\n\n/**\n * A text-based value type world renderer for operators.\n * @author rubensworks\n */\npublic class OperatorValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    private static final int MAX_LINES = 30;\n    private static final float MAX = 12.5F;\n    private static final float MARGIN_FACTOR = 1.1F;\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Font fontRenderer = context.font();\n        float maxWidth = 0;\n\n        ValueTypeOperator.ValueOperator valueOperator = ((ValueTypeOperator.ValueOperator) value);\n        IOperator operator = valueOperator.getRawValue();\n        List<Component> lines = Lists.newLinkedList();\n        lines.add(Component.literal(ValueTypes.OPERATOR.getName(valueOperator) + \" ::\"));\n        lines.addAll(ValueTypeOperator.getSignatureLines(operator, true));\n        for (Component line : lines) {\n            float width = fontRenderer.width(line.getString()) - 1;\n            maxWidth = Math.max(maxWidth, width);\n        }\n\n        float singleHeight = fontRenderer.lineHeight;\n        float totalHeight = singleHeight * lines.size();\n\n        matrixStack.pushPose();\n\n        float scaleX = MAX / (maxWidth * MARGIN_FACTOR);\n        float scaleY = MAX / (totalHeight * MARGIN_FACTOR);\n        float scale = Math.min(scaleX, scaleY); // Maintain aspect ratio\n        float newWidth = maxWidth * scale;\n        float newHeight = totalHeight * scale;\n        matrixStack.translate((MAX - newWidth) / 2, (MAX - newHeight) / 2, 0F);\n        matrixStack.scale(scale, scale, 1F);\n\n        int offset = 0;\n        for(Component line : lines) {\n            int color = IModHelpers.get().getBaseHelpers().addAlphaToColor(ValueTypes.OPERATOR.getDisplayColor(), alpha);\n            nodeCollector.submitText(matrixStack, 0, offset, Component.literal(line.getString()).getVisualOrderText(),\n                    false, Font.DisplayMode.NORMAL, combinedLight, color,\n                    0, 0);\n            offset += singleHeight;\n        }\n\n        matrixStack.popPose();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/RecipeValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;\nimport org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.client.render.part.DisplayPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * A value type world renderer for blocks.\n * @author rubensworks\n */\npublic class RecipeValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    private static final IValueTypeWorldRenderer INGREDIENTS_RENDERER = ValueTypeWorldRenderers.REGISTRY\n            .getRenderer(ValueTypes.OBJECT_INGREDIENTS);\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Optional<IRecipeDefinition> recipeOptional = ((ValueObjectTypeRecipe.ValueRecipe) value).getRawValue();\n        if(recipeOptional.isPresent()) {\n            IRecipeDefinition recipe = recipeOptional.get();\n\n            matrixStack.pushPose();\n            matrixStack.scale(0.5F, 0.5F, 1F);\n\n            matrixStack.pushPose();\n            matrixStack.scale(0.3F, 0.3F, 1F);\n            nodeCollector.submitText(matrixStack, 8, 15, Component.translatable(\"gui.integrateddynamics.input_short\").getVisualOrderText(), false, Font.DisplayMode.NORMAL, combinedLight, IModHelpers.get().getBaseHelpers().RGBToInt(255, 255, 255), 0, 0);\n            nodeCollector.submitText(matrixStack, 8, 15, Component.translatable(\"gui.integrateddynamics.input_short\").getVisualOrderText(), false, Font.DisplayMode.NORMAL, combinedLight, IModHelpers.get().getBaseHelpers().RGBToInt(255, 255, 255), 0, 0);\n            nodeCollector.submitText(matrixStack, 46, 15, Component.translatable(\"gui.integrateddynamics.output_short\").getVisualOrderText(), false, Font.DisplayMode.NORMAL, combinedLight, IModHelpers.get().getBaseHelpers().RGBToInt(255, 255, 255), 0, 0);\n            matrixStack.popPose();\n\n            matrixStack.translate(0, 2 * DisplayPartOverlayRenderer.MAX / 3, 0);\n            renderInput(context, partContainer, direction, partType, recipe, partialTicks,\n                    matrixStack, nodeCollector, combinedLight, combinedOverlay, alpha);\n            matrixStack.translate(DisplayPartOverlayRenderer.MAX, 0, 0);\n            INGREDIENTS_RENDERER.submitValue(context, partContainer, direction, partType,\n                    ValueObjectTypeIngredients.ValueIngredients.of(recipe.getOutput()), partialTicks,\n                    matrixStack, nodeCollector, combinedLight, combinedOverlay, alpha);\n\n            matrixStack.popPose();\n        }\n    }\n\n    protected void renderInput(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                               Direction direction, IPartType partType, IRecipeDefinition recipe, float partialTicks,\n                               PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                               int combinedLight, int combinedOverlay, float alpha) {\n        // Get a list of all values\n        int ingredientCount = recipe.getInputComponents().stream().mapToInt((c) -> recipe.getInputs(c).size()).sum();\n        List<IValue> values = Lists.newArrayListWithExpectedSize(ingredientCount);\n\n        // For ingredients with multiple possibilities, vary them based on the current tick\n        int tick = ((int) Minecraft.getInstance().level.getGameTime()) / 30;\n        for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {\n            IIngredientMatcher<?, ?> matcher = component.getMatcher();\n            IIngredientComponentHandler componentHandler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n            Stream<List<IPrototypedIngredient>> inputs = enhanceRecipeInputs(component, recipe);\n            inputs.forEach(element ->\n                    values.add(componentHandler.toValue(IngredientsValueTypeWorldRenderer.prepareElementForTick(\n                            element, tick, () -> new PrototypedIngredient(component, matcher.getEmptyInstance(), matcher.getAnyMatchCondition())).getPrototype())));\n        }\n\n        // Render ingredients in a square matrix\n        IngredientsValueTypeWorldRenderer.renderGrid(context, partContainer, direction, partType, values,\n                partialTicks, matrixStack, nodeCollector, combinedLight, combinedOverlay, alpha);\n    }\n\n    protected <T, M> Stream<List<IPrototypedIngredient>> enhanceRecipeInputs(IngredientComponent<T, M> ingredientComponent,\n                                                                             IRecipeDefinition recipe) {\n        Stream<IPrototypedIngredientAlternatives<T, M>> inputs = recipe.getInputs(ingredientComponent).stream();\n        if (ingredientComponent == IngredientComponent.ITEMSTACK) {\n            IIngredientMatcher<ItemStack, Integer> matcher = (IIngredientMatcher<ItemStack, Integer>) ingredientComponent.getMatcher();\n            return ((Stream<IPrototypedIngredientAlternatives<ItemStack, Integer>>) (Stream) inputs).map(input -> input\n                    .getAlternatives().stream()\n                    .map(prototypedIngredient -> Collections.singletonList(prototypedIngredient))\n                    .flatMap(List::stream)\n                    .collect(Collectors.toList())\n            );\n        } else {\n            return ((Stream<IPrototypedIngredientAlternatives<?, ?>>) (Stream) inputs)\n                    .map(p -> Lists.newArrayList(p.getAlternatives()));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/TextValueTypeWorldRenderer.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.client.render.part.DisplayPartOverlayRenderer;\n\n/**\n * A simple text-based value type world renderer.\n * @author rubensworks\n */\npublic class TextValueTypeWorldRenderer implements IValueTypeWorldRenderer {\n\n    private static final float MARGIN_FACTOR = 1.1F;\n\n    @Override\n    public void submitValue(BlockEntityRendererProvider.Context context, IPartContainer partContainer,\n                            Direction direction, IPartType partType, IValue value, float partialTicks,\n                            PoseStack matrixStack, SubmitNodeCollector nodeCollector,\n                            int combinedLight, int combinedOverlay, float alpha) {\n        Font fontRenderer = context.font();\n        float maxWidth = 0;\n\n        String[] lines = value.getType().toCompactString(value).getString().split(\"(?<=[^\\\\\\\\])\\\\\\\\n\");\n        for (String line : lines) {\n            float width = fontRenderer.width(polishLine(line)) - 1;\n            maxWidth = Math.max(maxWidth, width);\n        }\n\n        float singleHeight = fontRenderer.lineHeight;\n        float totalHeight = singleHeight * lines.length;\n\n        matrixStack.pushPose();\n\n        float scaleX = DisplayPartOverlayRenderer.MAX / (maxWidth * MARGIN_FACTOR);\n        float scaleY = DisplayPartOverlayRenderer.MAX / (totalHeight * MARGIN_FACTOR);\n        float scale = Math.min(scaleX, scaleY); // Maintain aspect ratio\n        float newWidth = maxWidth * scale;\n        float newHeight = totalHeight * scale;\n        matrixStack.translate((DisplayPartOverlayRenderer.MAX - newWidth) / 2, (DisplayPartOverlayRenderer.MAX - newHeight) / 2, 0F);\n        matrixStack.scale(scale, scale, 1F);\n\n        int offset = 0;\n        for(String line : lines) {\n            int color = IModHelpers.get().getBaseHelpers().addAlphaToColor(value.getType().getDisplayColor(), alpha);\n            nodeCollector.submitText(matrixStack, 0, offset, Component.literal(polishLine(line)).getVisualOrderText(), false, Font.DisplayMode.NORMAL, combinedLight, color, 0, 0);\n            offset += singleHeight;\n        }\n\n        matrixStack.popPose();\n    }\n\n    protected String polishLine(String line) {\n        return line.replaceAll(\"\\\\\\\\\\\\\\\\n\", \"\\\\\\\\n\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/ValueTypeWorldRendererRegistry.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRenderer;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRendererRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport java.util.IdentityHashMap;\nimport java.util.Map;\n\n/**\n * Registry for {@link IValueTypeWorldRenderer}.\n * @author rubensworks\n */\npublic final class ValueTypeWorldRendererRegistry implements IValueTypeWorldRendererRegistry {\n\n    private static ValueTypeWorldRendererRegistry INSTANCE = new ValueTypeWorldRendererRegistry();\n\n    private final Map<IValueType<?>, IValueTypeWorldRenderer> renderers = new IdentityHashMap<>();\n\n    private ValueTypeWorldRendererRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static ValueTypeWorldRendererRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <R extends IValueTypeWorldRenderer> R register(IValueType<?> valueType, R renderer) {\n        renderers.put(valueType, renderer);\n        return renderer;\n    }\n\n    @Override\n    public IValueTypeWorldRenderer getRenderer(IValueType<?> valueType) {\n        return renderers.get(valueType);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/client/render/valuetype/ValueTypeWorldRenderers.java",
    "content": "package org.cyclops.integrateddynamics.client.render.valuetype;\n\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.render.valuetype.IValueTypeWorldRendererRegistry;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * A collection of all value type world renderers.\n * @author rubensworks\n */\npublic class ValueTypeWorldRenderers {\n\n    public static final IValueTypeWorldRendererRegistry REGISTRY = IntegratedDynamics._instance.getRegistryManager().getRegistry(IValueTypeWorldRendererRegistry.class);\n\n    public static final TextValueTypeWorldRenderer DEFAULT = new TextValueTypeWorldRenderer();\n\n    public static void load() {\n        REGISTRY.register(ValueTypes.OBJECT_ITEMSTACK, new ItemValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.OBJECT_BLOCK, new BlockValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.OBJECT_FLUIDSTACK, new FluidValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.LIST, new ListValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.NBT, new NbtValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.OPERATOR, new OperatorValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.OBJECT_INGREDIENTS, new IngredientsValueTypeWorldRenderer());\n        REGISTRY.register(ValueTypes.OBJECT_RECIPE, new RecipeValueTypeWorldRenderer());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/command/CommandCrash.java",
    "content": "package org.cyclops.integrateddynamics.command;\n\nimport com.mojang.brigadier.Command;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.TickHandler;\n\n/**\n * A command to let the server crash.\n * @author rubensworks\n *\n */\npublic class CommandCrash implements Command<CommandSourceStack> {\n\n    @Override\n    public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {\n        IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, context.getSource().getTextName() + \" initialized a server crash.\");\n        TickHandler.getInstance().setShouldCrash();\n        return 0;\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> make() {\n        return Commands.literal(\"crash\")\n                .requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))\n                        .executes(new CommandCrash());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/command/CommandGenerateNetwork.java",
    "content": "package org.cyclops.integrateddynamics.command;\n\nimport com.mojang.brigadier.Command;\nimport com.mojang.brigadier.arguments.IntegerArgumentType;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.command.argument.ArgumentTypeEnum;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.block.BlockCable;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypeRegistry;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.cyclops.integrateddynamics.part.aspect.read.AspectReadBuilders;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * Command for generating networks with different presets.\n * @author rubensworks\n */\npublic class CommandGenerateNetwork implements Command<CommandSourceStack> {\n\n    public static LiteralArgumentBuilder<CommandSourceStack> make() {\n        LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(\"generatenetwork\")\n                .requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS));\n\n        // Add the preset subcommand with optional size/radius argument\n        builder.then(Commands.argument(\"preset\", new ArgumentTypeEnum(NetworkPreset.class))\n                .executes(new CommandGenerateNetworkExecutor(true, false))\n                .then(Commands.argument(\"size\", IntegerArgumentType.integer(1, 1000))\n                        .executes(new CommandGenerateNetworkExecutor(true, true))));\n\n        return builder;\n    }\n\n    @Override\n    public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {\n        context.getSource().sendFailure(Component.literal(\"Please specify a preset: empty, idle, redstoneioclock, or clear\")\n                .withStyle(ChatFormatting.RED));\n        return 0;\n    }\n\n    public enum NetworkPreset {\n        EMPTY,\n        IDLE,\n        REDSTONEIOCLOCK,\n        REDSTONEIOCLOCKVARIABLES,\n        CLEAR\n    }\n\n    /**\n     * Executor for the generatenetwork command.\n     */\n    public static class CommandGenerateNetworkExecutor implements Command<CommandSourceStack> {\n        private final boolean hasPreset;\n        private final boolean hasSize;\n\n        public CommandGenerateNetworkExecutor(boolean hasPreset, boolean hasSize) {\n            this.hasPreset = hasPreset;\n            this.hasSize = hasSize;\n        }\n\n        @Override\n        public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {\n            if (!hasPreset) {\n                context.getSource().sendFailure(Component.literal(\"Please specify a preset: empty, idle, redstoneioclock, or clear\")\n                        .withStyle(ChatFormatting.RED));\n                return 0;\n            }\n\n            NetworkPreset preset = ArgumentTypeEnum.getValue(context, \"preset\", NetworkPreset.class);\n            ServerLevel level = context.getSource().getLevel();\n            BlockPos playerPos = BlockPos.containing(context.getSource().getPosition());\n            int size = hasSize ? IntegerArgumentType.getInteger(context, \"size\") : getDefaultSize(preset);\n\n            switch (preset) {\n                case EMPTY:\n                    context.getSource().sendSuccess(\n                            () -> Component.literal(\"Generating network preset: empty (size: \" + size + \"x\" + size + \"x\" + size + \")\")\n                                    .withStyle(ChatFormatting.GREEN),\n                            true);\n                    NetworkGenerationHelper.generateEmptyNetwork(level, playerPos.above(2), size);\n                    break;\n                case IDLE:\n                    context.getSource().sendSuccess(\n                            () -> Component.literal(\"Generating network preset: idle (size: \" + size + \"x\" + size + \"x\" + size + \")\")\n                                    .withStyle(ChatFormatting.GREEN),\n                            true);\n                    NetworkGenerationHelper.generateIdleNetwork(level, playerPos.above(2), size);\n                    break;\n                case REDSTONEIOCLOCK:\n                    context.getSource().sendSuccess(\n                            () -> Component.literal(\"Generating network preset: redstoneioclock (size: \" + size + \"x\" + size + \"x\" + size + \")\")\n                                    .withStyle(ChatFormatting.GREEN),\n                            true);\n                    NetworkGenerationHelper.generateRedstoneNetwork(level, playerPos.above(2), size);\n                    break;\n                case REDSTONEIOCLOCKVARIABLES:\n                    context.getSource().sendSuccess(\n                            () -> Component.literal(\"Generating network preset: redstoneioclockvariables (size: \" + size + \"x\" + size + \"x\" + size + \")\")\n                                    .withStyle(ChatFormatting.GREEN),\n                            true);\n                    NetworkGenerationHelper.generateRedstoneNetworkVariables(level, playerPos.above(2), size);\n                    break;\n                case CLEAR:\n                    context.getSource().sendSuccess(\n                            () -> Component.literal(\"Clearing cables within radius: \" + size)\n                                    .withStyle(ChatFormatting.GREEN),\n                            true);\n                    NetworkGenerationHelper.clearCables(level, playerPos, size);\n                    break;\n            }\n\n            return 1;\n        }\n\n        /**\n         * Get the default size/radius for the given preset.\n         */\n        private int getDefaultSize(NetworkPreset preset) {\n            return preset == NetworkPreset.CLEAR ? 50 : 25;\n        }\n    }\n\n    /**\n     * Helper class for network generation logic, shared between command and game tests.\n     */\n    public static class NetworkGenerationHelper {\n        /**\n         * Generate a size x size x size cube of only logic cables.\n         */\n        public static void generateEmptyNetwork(ServerLevel level, BlockPos startPos, int size) {\n            List<BlockPos> placedPositions = new ArrayList<>();\n\n            BlockCable.SKIP_NETWORK_INIT = true;\n            try {\n                for (int x = 0; x < size; x++) {\n                    for (int y = 0; y < size; y++) {\n                        for (int z = 0; z < size; z++) {\n                            BlockPos pos = startPos.offset(x, y, z);\n                            level.setBlock(pos, RegistryEntries.BLOCK_CABLE.value().defaultBlockState(), 2);\n                            placedPositions.add(pos);\n                        }\n                    }\n                }\n            } finally {\n                BlockCable.SKIP_NETWORK_INIT = false;\n            }\n\n            for (BlockPos pos : placedPositions) {\n                CableHelpers.updateConnectionsNeighbours(level, pos, CableHelpers.ALL_SIDES);\n            }\n\n            NetworkHelpers.initNetwork(level, startPos, null);\n        }\n\n        /**\n         * Generate a size x size x size cube of logic cables where all cables on the outer sides\n         * contain random parts facing outwards.\n         */\n        public static void generateIdleNetwork(ServerLevel level, BlockPos startPos, int size) {\n            generateEmptyNetwork(level, startPos, size);\n\n            Random random = new Random();\n            List<BlockPos> updatePositions = new ArrayList<>();\n\n            addPartsToFace(level, startPos, size, 0, size - 1, size - 1, size - 1, 0, size - 1, Direction.UP, random, updatePositions);\n            addPartsToFace(level, startPos, size, 0, size - 1, 0, 0, 0, size - 1, Direction.DOWN, random, updatePositions);\n            addPartsToFace(level, startPos, size, 0, size - 1, 0, size - 1, 0, 0, Direction.NORTH, random, updatePositions);\n            addPartsToFace(level, startPos, size, 0, size - 1, 0, size - 1, size - 1, size - 1, Direction.SOUTH, random, updatePositions);\n            addPartsToFace(level, startPos, size, 0, 0, 0, size - 1, 0, size - 1, Direction.WEST, random, updatePositions);\n            addPartsToFace(level, startPos, size, size - 1, size - 1, 0, size - 1, 0, size - 1, Direction.EAST, random, updatePositions);\n\n            for (BlockPos pos : updatePositions) {\n                level.updateNeighborsAt(pos, RegistryEntries.BLOCK_CABLE.value());\n            }\n        }\n\n        /**\n         * Place a single cable block at the given position.\n         */\n        public static void placeCable(ServerLevel level, BlockPos pos) {\n            level.setBlock(pos, RegistryEntries.BLOCK_CABLE.value().defaultBlockState(), 2);\n        }\n\n        /**\n         * Add a random part to the NORTH face of a cable at the given position.\n         */\n        public static void addPartToNorthFace(ServerLevel level, BlockPos pos) {\n            List<IPartType> partTypes = new ArrayList<>(PartTypeRegistry.getInstance().getPartTypes());\n\n            if (partTypes.isEmpty()) {\n                return;\n            }\n\n            Random random = new Random();\n            IPartType partType = partTypes.get(random.nextInt(partTypes.size()));\n            ItemStack itemStack = new ItemStack(partType.getItem());\n            PartHelpers.addPart(level, pos, Direction.NORTH, partType, itemStack);\n        }\n\n        /**\n         * Clear all cable blocks within a radius of the given position.\n         */\n        public static void clearCables(ServerLevel level, BlockPos centerPos, int radius) {\n            BlockCable.SKIP_NETWORK_INIT = true;\n\n            try {\n                for (int x = centerPos.getX() - radius; x <= centerPos.getX() + radius; x++) {\n                    for (int y = centerPos.getY() - radius; y <= centerPos.getY() + radius; y++) {\n                        for (int z = centerPos.getZ() - radius; z <= centerPos.getZ() + radius; z++) {\n                            BlockPos pos = new BlockPos(x, y, z);\n                            if (level.getBlockState(pos).getBlock() == RegistryEntries.BLOCK_CABLE.value()) {\n                                level.destroyBlock(pos, false);\n                            }\n                        }\n                    }\n                }\n            } finally {\n                BlockCable.SKIP_NETWORK_INIT = false;\n            }\n        }\n\n        private static void addPartsToFace(ServerLevel level, BlockPos startPos, int size,\n                                          int minX, int maxX, int minY, int maxY, int minZ, int maxZ,\n                                          Direction side, Random random, List<BlockPos> updatePositions) {\n            for (int x = minX; x <= maxX; x++) {\n                for (int y = minY; y <= maxY; y++) {\n                    for (int z = minZ; z <= maxZ; z++) {\n                        BlockPos pos = startPos.offset(x, y, z);\n                        addRandomPartDeferred(level, pos, side, random, updatePositions);\n                    }\n                }\n            }\n        }\n\n        private static void addRandomPartDeferred(ServerLevel level, BlockPos pos, Direction side, Random random, List<BlockPos> updatePositions) {\n            List<IPartType> partTypes = new ArrayList<>(PartTypeRegistry.getInstance().getPartTypes());\n\n            if (partTypes.isEmpty()) {\n                return;\n            }\n\n            IPartType partType = partTypes.get(random.nextInt(partTypes.size()));\n            ItemStack itemStack = new ItemStack(partType.getItem());\n            PartHelpers.addPart(level, pos, side, partType, itemStack);\n            updatePositions.add(pos);\n        }\n\n        /**\n         * Generate a size x size x size cube of logic cables where all cables on the EAST side\n         * contain redstone readers, and all cables on the WEST side contain redstone writers.\n         * For each reader-writer pair at the same Y and Z coordinates, a variable is created\n         * that reads the BOOLEAN_CLOCK aspect from the reader and connects it to the\n         * BOOLEAN aspect of the writer at the opposite side.\n         */\n        public static void generateRedstoneNetwork(ServerLevel level, BlockPos startPos, int size) {\n            generateEmptyNetwork(level, startPos, size);\n\n            List<BlockPos> updatePositions = new ArrayList<>();\n\n            // Add redstone readers to EAST side and redstone writers to WEST side\n            // EAST side is at x = size - 1, WEST side is at x = 0\n            for (int y = 0; y < size; y++) {\n                for (int z = 0; z < size; z++) {\n                    // EAST side: redstone reader\n                    BlockPos eastPos = startPos.offset(size - 1, y, z);\n                    PartHelpers.addPart(level, eastPos, Direction.EAST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n                    updatePositions.add(eastPos);\n\n                    // WEST side: redstone writer\n                    BlockPos westPos = startPos.offset(0, y, z);\n                    PartHelpers.addPart(level, westPos, Direction.WEST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n                    updatePositions.add(westPos);\n                }\n            }\n\n            // Update all positions and create variable connections\n            for (BlockPos pos : updatePositions) {\n                level.updateNeighborsAt(pos, RegistryEntries.BLOCK_CABLE.value());\n            }\n\n            // Create variables connecting readers to writers\n            for (int y = 0; y < size; y++) {\n                for (int z = 0; z < size; z++) {\n                    BlockPos eastPos = startPos.offset(size - 1, y, z);\n                    BlockPos westPos = startPos.offset(0, y, z);\n\n                    // Create variable from reader's BOOLEAN_CLOCK aspect\n                    org.cyclops.integrateddynamics.api.part.PartPos eastPartPos = org.cyclops.integrateddynamics.api.part.PartPos.of(level, eastPos, Direction.EAST);\n                    PartHelpers.PartStateHolder<?, ?> eastPartStateHolder = PartHelpers.getPart(eastPartPos);\n                    if (eastPartStateHolder != null) {\n                        ItemStack variableCard = GameTestHelpersIntegratedDynamics.createVariableFromReader(level,\n                                Aspects.Read.Redstone.BOOLEAN_CLOCK, eastPartStateHolder.getState());\n\n                        // Place variable in writer's BOOLEAN aspect\n                        org.cyclops.integrateddynamics.api.part.PartPos westPartPos = org.cyclops.integrateddynamics.api.part.PartPos.of(level, westPos, Direction.WEST);\n                        GameTestHelpersIntegratedDynamics.placeVariableInWriter(null, level, westPartPos,\n                                Aspects.Write.Redstone.BOOLEAN, variableCard);\n                    }\n                }\n            }\n        }\n\n        /**\n         * Generate a size x size x size cube of logic cables where all cables on the EAST side\n         * contain redstone readers, and all cables on the WEST side contain redstone writers.\n         * For each reader-writer pair at the same Y and Z coordinates, a CHOICE operator is created\n         * that reads the BOOLEAN_CLOCK aspect from the reader and chooses between constants 0 and 10.\n         * The result is written to the INTEGER aspect of the writer.\n         * Variable cards are stored in variable store blocks placed on the SOUTH side of the network,\n         * stacked vertically. Each variable store can hold multiple CHOICE operator configurations.\n         * All redstone readers have PROPERTY_LENGTH set to 10.\n         */\n        public static void generateRedstoneNetworkVariables(ServerLevel level, BlockPos startPos, int size) {\n            generateEmptyNetwork(level, startPos, size);\n\n            List<BlockPos> updatePositions = new ArrayList<>();\n\n            // Add redstone readers to EAST side and redstone writers to WEST side\n            for (int y = 0; y < size; y++) {\n                for (int z = 0; z < size; z++) {\n                    // EAST side: redstone reader\n                    BlockPos eastPos = startPos.offset(size - 1, y, z);\n                    PartHelpers.addPart(level, eastPos, Direction.EAST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n                    updatePositions.add(eastPos);\n\n                    // WEST side: redstone writer\n                    BlockPos westPos = startPos.offset(0, y, z);\n                    PartHelpers.addPart(level, westPos, Direction.WEST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n                    updatePositions.add(westPos);\n                }\n            }\n\n            // Update all positions\n            for (BlockPos pos : updatePositions) {\n                level.updateNeighborsAt(pos, RegistryEntries.BLOCK_CABLE.value());\n            }\n\n            // Create variable stores on the SOUTH side of the network\n            // Place stores at (z = size, y varying) stacked vertically\n            // Each store can hold 4 items: clock variable, constant 0, constant 10, and choice operator\n            int storeX = startPos.getX(); // Aligned with the network\n            int storeZ = startPos.getZ() + size; // SOUTH side\n            int currentStoreY = startPos.getY();\n            int currentSlot = 0;\n            BlockEntityVariablestore currentVariableStore = null;\n            BlockPos currentStorePos = null;\n\n            // Create variables connecting readers to writers using CHOICE operator\n            for (int y = 0; y < size; y++) {\n                for (int z = 0; z < size; z++) {\n                    BlockPos eastPos = startPos.offset(size - 1, y, z);\n                    BlockPos westPos = startPos.offset(0, y, z);\n\n                    // Get or create a new variable store if current one is full\n                    if (currentVariableStore == null || currentSlot >= BlockEntityVariablestore.INVENTORY_SIZE) {\n                        if (currentSlot >= BlockEntityVariablestore.INVENTORY_SIZE) {\n                            // Current store is full, move to next store (stack vertically)\n                            currentStoreY++;\n                        }\n\n                        currentStorePos = new BlockPos(storeX, currentStoreY, storeZ);\n                        level.setBlock(currentStorePos, RegistryEntries.BLOCK_VARIABLE_STORE.get().defaultBlockState(), 2);\n                        currentVariableStore = (BlockEntityVariablestore) level.getBlockEntity(currentStorePos);\n                        currentSlot = 0;\n                    }\n\n                    if (currentVariableStore != null) {\n                        // Create variable from reader's BOOLEAN_CLOCK aspect\n                        org.cyclops.integrateddynamics.api.part.PartPos eastPartPos = org.cyclops.integrateddynamics.api.part.PartPos.of(level, eastPos, Direction.EAST);\n                        PartHelpers.PartStateHolder<?, ?> eastPartStateHolder = PartHelpers.getPart(eastPartPos);\n\n                        if (eastPartStateHolder != null) {\n                            // Create constant integer variables (0 and 10) - reuse from first slot if already created\n                            ItemStack variable0, variable10;\n                            int variable0Id, variable10Id;\n\n                            int currentSlotIncrement;\n                            if (currentSlot == 0) {\n                                // First time, create and store constants\n                                variable0 = GameTestHelpersIntegratedDynamics.createVariableForValue(level, ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n                                variable10 = GameTestHelpersIntegratedDynamics.createVariableForValue(level, ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(10));\n                                currentVariableStore.getInventory().setItem(1, variable0);\n                                currentVariableStore.getInventory().setItem(2, variable10);\n                                variable0Id = GameTestHelpersIntegratedDynamics.getVariableFacade(level, variable0).getId();\n                                variable10Id = GameTestHelpersIntegratedDynamics.getVariableFacade(level, variable10).getId();\n                                currentSlotIncrement = 4;\n                            } else {\n                                // Reuse constants from slots 1 and 2\n                                variable0Id = GameTestHelpersIntegratedDynamics.getVariableFacade(level, currentVariableStore.getInventory().getItem(1)).getId();\n                                variable10Id = GameTestHelpersIntegratedDynamics.getVariableFacade(level, currentVariableStore.getInventory().getItem(2)).getId();\n                                currentSlotIncrement = 2;\n                            }\n\n                            // Create variable from reader's BOOLEAN_CLOCK aspect\n                            ItemStack variableClock = GameTestHelpersIntegratedDynamics.createVariableFromReader(level,\n                                    Aspects.Read.Redstone.BOOLEAN_CLOCK, eastPartStateHolder.getState());\n                            currentVariableStore.getInventory().setItem(currentSlot, variableClock);\n\n                            // Create CHOICE operator variable\n                            ItemStack variableChoice = GameTestHelpersIntegratedDynamics.createVariableForOperator(level, Operators.GENERAL_CHOICE, new int[]{\n                                    GameTestHelpersIntegratedDynamics.getVariableFacade(level, variableClock).getId(),\n                                    variable0Id,\n                                    variable10Id\n                            });\n                            currentVariableStore.getInventory().setItem(currentSlot + currentSlotIncrement - 1, variableChoice);\n\n                            // Place CHOICE variable in writer's INTEGER aspect\n                            org.cyclops.integrateddynamics.api.part.PartPos westPartPos = org.cyclops.integrateddynamics.api.part.PartPos.of(level, westPos, Direction.WEST);\n                            GameTestHelpersIntegratedDynamics.placeVariableInWriter(null, level, westPartPos,\n                                    Aspects.Write.Redstone.INTEGER, variableChoice);\n\n                            currentSlot += currentSlotIncrement;\n                        }\n                    }\n                }\n            }\n\n            // Set PROPERTY_LENGTH to 10 for all redstone readers\n            for (int y = 0; y < size; y++) {\n                for (int z = 0; z < size; z++) {\n                    BlockPos eastPos = startPos.offset(size - 1, y, z);\n                    PartPos eastPartPos = PartPos.of(level, eastPos, Direction.EAST);\n                    GameTestHelpersIntegratedDynamics.setAspectProperty(eastPartPos, Aspects.Read.Redstone.BOOLEAN_CLOCK, AspectReadBuilders.Redstone.PROPERTY_LENGTH, ValueTypeInteger.ValueInteger.of(10));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/command/CommandNetworkDiagnostics.java",
    "content": "package org.cyclops.integrateddynamics.command;\n\nimport com.mojang.brigadier.Command;\nimport com.mojang.brigadier.arguments.IntegerArgumentType;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport org.cyclops.cyclopscore.command.argument.ArgumentTypeEnum;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;\nimport org.cyclops.integrateddynamics.network.packet.NetworkDiagnosticsTriggerClient;\n\n/**\n * Command for opening the network diagnostics gui.\n * @author rubensworks\n *\n */\npublic class CommandNetworkDiagnostics implements Command<CommandSourceStack> {\n\n    private final boolean operationArg;\n    private final boolean portArg;\n\n    public CommandNetworkDiagnostics(boolean operationArg, boolean portArg) {\n        this.operationArg = operationArg;\n        this.portArg = portArg;\n    }\n\n    @Override\n    public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {\n        StartStop operation = this.operationArg ? ArgumentTypeEnum.getValue(context, \"operation\", StartStop.class) : StartStop.START;\n        int port = this.portArg ? IntegerArgumentType.getInteger(context, \"port\") : GeneralConfig.diagnosticsWebServerPort;\n        IntegratedDynamics._instance.getPacketHandler().sendToPlayer\n                (new NetworkDiagnosticsTriggerClient(operation == StartStop.START, port),\n                context.getSource().getPlayerOrException()\n        );\n        return 0;\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> make() {\n        LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(\"networkdiagnostics\")\n                .requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))\n                .executes(new CommandNetworkDiagnostics(false, false));\n\n        // Add the operation/port subcommand chain\n        builder.then(Commands.argument(\"operation\", new ArgumentTypeEnum(StartStop.class))\n                .executes(new CommandNetworkDiagnostics(true, false))\n                .then(Commands.argument(\"port\", IntegerArgumentType.integer())\n                    .executes(new CommandNetworkDiagnostics(true, true))));\n\n        // Add the measure subcommand\n        builder.then(Commands.literal(\"measure\")\n                .executes(new CommandMeasure(false))\n                .then(Commands.argument(\"seconds\", IntegerArgumentType.integer(1, 600))\n                        .executes(new CommandMeasure(true))));\n\n        return builder;\n    }\n\n    public static enum StartStop {\n        START,\n        STOP;\n    }\n\n    /**\n     * Subcommand for measuring network tick times.\n     */\n    public static class CommandMeasure implements Command<CommandSourceStack> {\n        private final boolean hasSecondsArg;\n\n        public CommandMeasure(boolean hasSecondsArg) {\n            this.hasSecondsArg = hasSecondsArg;\n        }\n\n        @Override\n        public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {\n            int seconds = hasSecondsArg ? IntegerArgumentType.getInteger(context, \"seconds\") : 10;\n            NetworkDiagnostics.getInstance().startMeasurement(context.getSource().getPlayerOrException(), seconds);\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/command/CommandTest.java",
    "content": "package org.cyclops.integrateddynamics.command;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.mojang.brigadier.Command;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Command for initiating the integration tests.\n * @author rubensworks\n *\n */\npublic class CommandTest implements Command<CommandSourceStack> {\n\n    public static final String P = \"org.cyclops.integrateddynamics.gametest.integration.\";\n    public static final List<String> CLASSES = Lists.newArrayList(\n            P + \"TestVariables\",\n            P + \"TestBlockOperators\",\n            P + \"TestItemStackOperators\",\n            P + \"TestEntityOperators\",\n            P + \"TestFluidStackOperators\",\n            P + \"TestIngredientsOperators\",\n            P + \"TestRecipeOperators\"\n    );\n\n    @Override\n    public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {\n        context.getSource().getPlayerOrException().sendSystemMessage(Component.literal(\"Running tests...\"));\n        try {\n            if(!test()) {\n                context.getSource().getPlayerOrException().sendSystemMessage(Component.literal(\"There were failing tests, see results in console.\"));\n            } else {\n                context.getSource().getPlayerOrException().sendSystemMessage(Component.literal(\"All tests succeeded!\"));\n            }\n            return 0;\n        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {\n            e.printStackTrace();\n            return 1;\n        }\n    }\n\n    protected boolean test() throws IllegalAccessException, InstantiationException, InvocationTargetException {\n        int ok = 0;\n        int total = 0;\n        for(String className : CLASSES) {\n            try {\n                Class<?> clazz = Class.forName(className);\n                Object testInstance = clazz.newInstance();\n\n                // Collect test methods\n                List<Method> befores = Lists.newLinkedList();\n                Map<Method, Boolean> tests = Maps.newHashMap();\n                for (Method method : clazz.getDeclaredMethods()) {\n                    if (method.isAnnotationPresent(IntegrationBefore.class)) {\n                        befores.add(method);\n                    }\n                    if (method.isAnnotationPresent(IntegrationTest.class)) {\n                        tests.put(method, false);\n                    }\n                }\n\n                // Run tests\n                for (Method test : tests.keySet()) {\n                    String testName = className.replace(P, \"\") + \"#\" + test.getName();\n                    for (Method before : befores) {\n                        before.invoke(testInstance);\n                    }\n                    boolean testOk;\n                    try {\n                        test.invoke(testInstance);\n                        testOk = true;\n                    } catch (InvocationTargetException e) {\n                        Class<?> excepted = test.getAnnotation(IntegrationTest.class).expected();\n                        if (!excepted.isInstance(e.getTargetException())) {\n                            testOk = false;\n                            if (e.getTargetException() instanceof IllegalStateException || e.getTargetException() instanceof AssertionError) {\n                                System.err.println(\"Test \" + testName + \" failed!\");\n                                e.getTargetException().printStackTrace();\n                            } else {\n                                System.err.println(String.format(\"Expected at %s exception %s, but found:\", testName, e));\n                                e.getTargetException().printStackTrace();\n                            }\n                        } else {\n                            testOk = true;\n                        }\n                    }\n                    tests.put(test, testOk);\n                }\n\n                // Count results\n                for (Boolean result : tests.values()) {\n                    if (result) {\n                        ok++;\n                    }\n                }\n                total += tests.size();\n            } catch (ClassNotFoundException e) {\n                System.err.println(\"Skipped test class: \" + className);\n            }\n        }\n        System.err.println(String.format(\"Tests succeeded: %s/%s\", ok, total));\n        return ok == total;\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> make() {\n        return Commands.literal(\"test\")\n                .requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))\n                .executes(new CommandTest());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentActivatedConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport com.mojang.serialization.Codec;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentActivatedConfig extends DataComponentConfigCommon<Boolean, IntegratedDynamics> {\n    public DataComponentActivatedConfig() {\n        super(IntegratedDynamics._instance, \"activated\", builder -> builder\n                .persistent(Codec.BOOL)\n                .networkSynchronized(ByteBufCodecs.BOOL));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentFacadeBlockConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentFacadeBlockConfig extends DataComponentConfigCommon<BlockState, IntegratedDynamics> {\n\n    public DataComponentFacadeBlockConfig() {\n        super(IntegratedDynamics._instance, \"facade_block\", builder -> builder\n                .persistent(BlockState.CODEC)\n                .networkSynchronized(ByteBufCodecs.fromCodec(BlockState.CODEC)));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentFluidContentInOutConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.neoforged.neoforge.fluids.SimpleFluidContent;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentFluidContentInOutConfig extends DataComponentConfigCommon<Pair<SimpleFluidContent, SimpleFluidContent>, IntegratedDynamics> {\n    public DataComponentFluidContentInOutConfig() {\n        super(IntegratedDynamics._instance, \"fluid_content_in_out\", builder -> builder\n                .persistent(RecordCodecBuilder.create(rb ->\n                        rb.group(\n                            SimpleFluidContent.CODEC.fieldOf(\"in\").forGetter(Pair::getLeft),\n                            SimpleFluidContent.CODEC.fieldOf(\"out\").forGetter(Pair::getRight)\n                    ).apply(rb, Pair::of)\n                ))\n                .networkSynchronized(StreamCodec.composite(\n                        SimpleFluidContent.STREAM_CODEC, Pair::getLeft,\n                        SimpleFluidContent.STREAM_CODEC, Pair::getRight,\n                        Pair::of\n                )));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentOmnidirectionalGroupConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport com.mojang.serialization.Codec;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentOmnidirectionalGroupConfig extends DataComponentConfigCommon<Integer, IntegratedDynamics> {\n\n    public DataComponentOmnidirectionalGroupConfig() {\n        super(IntegratedDynamics._instance, \"omnidirectional_group\", builder -> builder\n                .persistent(Codec.INT)\n                .networkSynchronized(ByteBufCodecs.INT));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentPartEnhancementConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport com.mojang.serialization.Codec;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentPartEnhancementConfig extends DataComponentConfigCommon<Integer, IntegratedDynamics> {\n\n    public DataComponentPartEnhancementConfig() {\n        super(IntegratedDynamics._instance, \"part_enhancement\", builder -> builder\n                .persistent(Codec.INT)\n                .networkSynchronized(ByteBufCodecs.INT));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentPartStateConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentPartStateConfig extends DataComponentConfigCommon<CompoundTag, IntegratedDynamics> {\n\n    public DataComponentPartStateConfig() {\n        super(IntegratedDynamics._instance, \"part_state\", builder -> builder\n                .persistent(Codecs.COMPOUND_TAG)\n                .networkSynchronized(ByteBufCodecs.COMPOUND_TAG));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentProxyIdConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport com.mojang.serialization.Codec;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentProxyIdConfig extends DataComponentConfigCommon<Integer, IntegratedDynamics> {\n\n    public DataComponentProxyIdConfig() {\n        super(IntegratedDynamics._instance, \"proxy_id\", builder -> builder\n                .persistent(Codec.INT)\n                .networkSynchronized(ByteBufCodecs.INT));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentVariableFacadeConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentVariableFacadeConfig extends DataComponentConfigCommon<CompoundTag, IntegratedDynamics> {\n\n    public DataComponentVariableFacadeConfig() {\n        super(IntegratedDynamics._instance, \"variable_facade\", builder -> builder\n                .persistent(Codecs.COMPOUND_TAG)\n                .networkSynchronized(ByteBufCodecs.COMPOUND_TAG));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentWrenchModeConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.item.ItemWrench;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentWrenchModeConfig extends DataComponentConfigCommon<ItemWrench.Mode, IntegratedDynamics> {\n\n    public DataComponentWrenchModeConfig() {\n        super(IntegratedDynamics._instance, \"wrench_mode\", builder -> builder\n                .persistent(ItemWrench.Mode.CODEC)\n                .networkSynchronized(ItemWrench.Mode.STREAM_CODEC));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentWrenchTargetBlockPosConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport net.minecraft.core.BlockPos;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentWrenchTargetBlockPosConfig extends DataComponentConfigCommon<BlockPos, IntegratedDynamics> {\n\n    public DataComponentWrenchTargetBlockPosConfig() {\n        super(IntegratedDynamics._instance, \"wrench_target_blockpos\", builder -> builder\n                .persistent(BlockPos.CODEC)\n                .networkSynchronized(BlockPos.STREAM_CODEC));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/component/DataComponentWrenchTargetDirectionConfig.java",
    "content": "package org.cyclops.integrateddynamics.component;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.config.extendedconfig.DataComponentConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class DataComponentWrenchTargetDirectionConfig extends DataComponentConfigCommon<Direction, IntegratedDynamics> {\n\n    public DataComponentWrenchTargetDirectionConfig() {\n        super(IntegratedDynamics._instance, \"wrench_target_direction\", builder -> builder\n                .persistent(Direction.CODEC)\n                .networkSynchronized(Direction.STREAM_CODEC));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/NoteBlockEventReceiver.java",
    "content": "package org.cyclops.integrateddynamics.core;\n\nimport com.google.common.collect.HashMultimap;\nimport com.google.common.collect.Multimap;\nimport net.minecraft.world.level.block.state.properties.NoteBlockInstrument;\nimport net.neoforged.bus.api.EventPriority;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.event.level.NoteBlockEvent;\nimport net.neoforged.neoforge.event.tick.ServerTickEvent;\n\n/**\n * Captures note block events for one tick.\n * @author rubensworks\n */\npublic final class NoteBlockEventReceiver {\n\n    private static NoteBlockEventReceiver INSTANCE;\n    private Multimap<NoteBlockInstrument, NoteBlockEvent.Play> previousEvents = HashMultimap.create();\n    private Multimap<NoteBlockInstrument, NoteBlockEvent.Play> currentEvents = HashMultimap.create();\n\n    private NoteBlockEventReceiver() {\n\n    }\n\n    public static NoteBlockEventReceiver getInstance() {\n        if(INSTANCE == null) {\n            INSTANCE = new NoteBlockEventReceiver();\n        }\n        return INSTANCE;\n    }\n\n    @SubscribeEvent(priority = EventPriority.LOWEST)\n    public void onNoteEvent(NoteBlockEvent.Play event) {\n        if(!event.isCanceled()) {\n            currentEvents.put(event.getInstrument(), event);\n        }\n    }\n\n    @SubscribeEvent\n    public void onTick(ServerTickEvent.Pre event) {\n        Multimap<NoteBlockInstrument, NoteBlockEvent.Play> tmp = previousEvents;\n        previousEvents.clear();\n        previousEvents = currentEvents;\n        currentEvents = tmp;\n    }\n\n    public Multimap<NoteBlockInstrument, NoteBlockEvent.Play> getEvents() {\n        return previousEvents;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/TickHandler.java",
    "content": "package org.cyclops.integrateddynamics.core;\n\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.event.tick.ServerTickEvent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.network.IFullNetworkListener;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.PartOffsetsClientNotifier;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;\nimport org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;\n\n/**\n * Handles server ticks to delegate to networks.\n * @author rubensworks\n */\npublic final class TickHandler {\n\n    private static TickHandler INSTANCE;\n    private int tick = 0;\n    private boolean shouldCrash = false;\n    public boolean ticked = false;\n\n    private TickHandler() {\n\n    }\n\n    public static TickHandler getInstance() {\n        if(INSTANCE == null) {\n            INSTANCE = new TickHandler();\n        }\n        return INSTANCE;\n    }\n\n    public void setShouldCrash() {\n        this.shouldCrash = true;\n    }\n\n    @SubscribeEvent\n    public void onTick(ServerTickEvent.Post event) {\n        if (shouldCrash) {\n            throw new RuntimeException(\"Forcefully crashed the server.\");\n        }\n        // Invoke update logic irrespective of safe-mode\n        for (INetwork network : NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getNetworks()) {\n            network.updateGuaranteed();\n        }\n\n        // Do further network updates only when safe-mode is not enabled\n        if (NetworkHelpers.shouldWork()) {\n            boolean isBeingDiagnozed = NetworkDiagnostics.getInstance().isBeingDiagnozed();\n            if (isBeingDiagnozed) {\n                tick = (tick + 1) % IModHelpers.get().getMinecraftHelpers().getSecondInTicks();\n                if (tick == 0) {\n                    NetworkDiagnostics.getInstance().accumulateMeasurements();\n                }\n            }\n            boolean shouldSendTickDurationInfo = isBeingDiagnozed && tick == 0;\n            for (INetwork network : NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getNetworks()) {\n                if (isBeingDiagnozed && (shouldSendTickDurationInfo || network.hasChanged())) {\n                    NetworkDiagnostics.getInstance().sendNetworkUpdate(network);\n\n                    network.resetLastSecondDurations();\n\n                    // Also reset durations of indexes\n                    for (IFullNetworkListener fullNetworkListener : network.getFullNetworkListeners()) {\n                        if (fullNetworkListener instanceof IPositionedAddonsNetworkIngredients) {\n                            IPositionedAddonsNetworkIngredients<?, ?> networkIngredients = (IPositionedAddonsNetworkIngredients<?, ?>) fullNetworkListener;\n                            networkIngredients.resetLastSecondDurationsIndex();\n                        }\n                    }\n                }\n                try {\n                    if (!network.isCrashed()) {\n                        network.update();\n                    }\n                } catch (Throwable e) {\n                    network.setCrashed(true);\n                    throw e;\n                }\n            }\n\n            if (isBeingDiagnozed) {\n                // Check if any measurements should complete\n                NetworkDiagnostics.getInstance().checkCompleteMeasurements();\n            }\n        }\n\n        ticked = true;\n\n        PartOffsetsClientNotifier.getInstance().tick();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/BlockContainerCabled.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Explosion;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelAccessor;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.block.BlockWithEntity;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.function.BiFunction;\n\n/**\n * A base block with part entity that can connect to cables.\n * @author rubensworks\n */\npublic abstract class BlockContainerCabled extends BlockWithEntity {\n\n    public BlockContainerCabled(Block.Properties properties, BiFunction<BlockPos, BlockState, CyclopsBlockEntity> blockEntitySupplier) {\n        super(properties, blockEntitySupplier);\n    }\n\n    @Override\n    public InteractionResult useItemOn(ItemStack heldItem, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand,\n                                       BlockHitResult blockRayTraceResult) {\n        if (!world.isClientSide() && WrenchHelpers.isWrench(player, heldItem, world, pos, blockRayTraceResult.getDirection()) && player.isSecondaryUseActive()) {\n            world.destroyBlock(pos, true);\n            return InteractionResult.SUCCESS;\n        }\n        return super.useItemOn(heldItem, state, world, pos, player, hand, blockRayTraceResult);\n    }\n\n    @Override\n    public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {\n        super.onPlace(state, world, pos, oldState, isMoving);\n        if (!world.isClientSide()) {\n            CableHelpers.onCableAdded(world, pos);\n        }\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        super.setPlacedBy(world, pos, state, placer, itemStack);\n        if (!world.isClientSide()) {\n            CableHelpers.onCableAddedByPlayer(world, pos, placer);\n        }\n    }\n\n    @Override\n    public void destroy(LevelAccessor world, BlockPos blockPos, BlockState blockState) {\n        CableHelpers.onCableRemoving((Level) world, blockPos, true, false, blockState, world.getBlockEntity(blockPos));\n        super.destroy(world, blockPos, blockState);\n        CableHelpers.onCableRemoved((Level) world, blockPos);\n    }\n\n    @Override\n    public void onBlockExploded(BlockState state, ServerLevel world, BlockPos blockPos, Explosion explosion) {\n        CableHelpers.setRemovingCable(true);\n        CableHelpers.onCableRemoving(world, blockPos, true, false, state, world.getBlockEntity(blockPos));\n        super.onBlockExploded(state, world, blockPos, explosion);\n        CableHelpers.onCableRemoved(world, blockPos);\n        CableHelpers.setRemovingCable(false);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public void neighborChanged(BlockState state, Level world, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean isMoving) {\n        super.neighborChanged(state, world, pos, neighborBlock, orientation, isMoving);\n        NetworkHelpers.onElementProviderBlockNeighborChange(world, pos, null);\n    }\n\n    @Override\n    public void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbor) {\n        super.onNeighborChange(state, world, pos, neighbor);\n        if (world instanceof Level) {\n            NetworkHelpers.onElementProviderBlockNeighborChange((Level) world, pos, null);\n        }\n    }\n\n    @Override\n    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {\n        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);\n\n        if (!CableHelpers.isRemovingCable()) {\n            CableHelpers.onCableRemoved(level, pos);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/BlockMechanicalMachine.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.RegistryEntries;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;\n\nimport java.util.function.BiFunction;\n\n/**\n * A mechanical machine base block\n * @author rubensworks\n */\npublic abstract class BlockMechanicalMachine extends BlockWithEntityGuiCabled {\n\n    public BlockMechanicalMachine(Properties properties, BiFunction<BlockPos, BlockState, CyclopsBlockEntity> blockEntitySupplier) {\n        super(properties, blockEntitySupplier);\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos blockPos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        if (!world.isClientSide()) {\n            IModHelpers.get().getBlockEntityHelpers().get(world, blockPos, BlockEntityMechanicalMachine.class)\n                    .ifPresent(tile -> {\n                        if (itemStack.has(RegistryEntries.COMPONENT_ENERGY_STORAGE)) {\n                            tile.getEnergyHandler().set(itemStack.get(RegistryEntries.COMPONENT_ENERGY_STORAGE));\n                        }\n                    });\n        }\n        super.setPlacedBy(world, blockPos, state, placer, itemStack);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/BlockRayTraceResultComponent.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport net.minecraft.world.phys.BlockHitResult;\n\n/**\n * A {@link BlockHitResult} that specifies the selected {@link VoxelShapeComponents.IComponent}.\n * @author rubensworks\n */\npublic class BlockRayTraceResultComponent extends BlockHitResult {\n\n    private final VoxelShapeComponents.IComponent component;\n\n    public BlockRayTraceResultComponent(BlockHitResult blockRayTraceResult, VoxelShapeComponents.IComponent component) {\n        super(blockRayTraceResult.getLocation(), blockRayTraceResult.getDirection(), blockRayTraceResult.getBlockPos(), blockRayTraceResult.isInside());\n        this.component = component;\n    }\n\n    public VoxelShapeComponents.IComponent getComponent() {\n        return component;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/BlockWithEntityGuiCabled.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Explosion;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelAccessor;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.block.BlockWithEntityGui;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.function.BiFunction;\n\n/**\n * A base block with a gui and part entity that can connect to cables.\n * @author rubensworks\n */\npublic abstract class BlockWithEntityGuiCabled extends BlockWithEntityGui {\n\n    public BlockWithEntityGuiCabled(Properties properties, BiFunction<BlockPos, BlockState, CyclopsBlockEntity> blockEntitySupplier) {\n        super(properties, blockEntitySupplier);\n    }\n\n    @Override\n    public InteractionResult useItemOn(ItemStack pStack, BlockState blockState, Level world, BlockPos blockPos, Player player, InteractionHand pHand, BlockHitResult rayTraceResult) {\n        ItemStack heldItem = player.getItemInHand(player.getUsedItemHand());\n        if (WrenchHelpers.isWrench(player, heldItem, world, blockPos, rayTraceResult.getDirection())\n                && player.isSecondaryUseActive()) {\n            if (!world.isClientSide()) {\n                Block.dropResources(blockState, world, blockPos, blockState.hasBlockEntity() ? world.getBlockEntity(blockPos) : null, player, heldItem);\n                world.destroyBlock(blockPos, false);\n            }\n            return InteractionResult.SUCCESS;\n        }\n        return super.useItemOn(pStack, blockState, world, blockPos, player, pHand, rayTraceResult);\n    }\n\n    @Override\n    public void onPlace(BlockState blockState, Level world, BlockPos blockPos, BlockState oldState, boolean isMoving) {\n        super.onPlace(blockState, world, blockPos, oldState, isMoving);\n        if (!world.isClientSide()) {\n            CableHelpers.onCableAdded(world, blockPos);\n        }\n    }\n\n    @Override\n    public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {\n        super.setPlacedBy(world, pos, state, placer, itemStack);\n        if (!world.isClientSide()) {\n            CableHelpers.onCableAddedByPlayer(world, pos, placer);\n        }\n    }\n\n    @Override\n    public void destroy(LevelAccessor world, BlockPos blockPos, BlockState blockState) {\n        CableHelpers.onCableRemoving((Level) world, blockPos, true, false, blockState, world.getBlockEntity(blockPos));\n        super.destroy(world, blockPos, blockState);\n        CableHelpers.onCableRemoved((Level) world, blockPos);\n    }\n\n    @Override\n    public void onBlockExploded(BlockState state, ServerLevel world, BlockPos blockPos, Explosion explosion) {\n        CableHelpers.setRemovingCable(true);\n        CableHelpers.onCableRemoving(world, blockPos, true, false, state, world.getBlockEntity(blockPos));\n        super.onBlockExploded(state, world, blockPos, explosion);\n        CableHelpers.onCableRemoved(world, blockPos);\n        CableHelpers.setRemovingCable(false);\n    }\n\n    @Override\n    protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) {\n        super.neighborChanged(state, level, pos, neighborBlock, orientation, movedByPiston);\n        NetworkHelpers.onElementProviderBlockNeighborChange(level, pos, null);\n    }\n\n    @Override\n    public void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbor) {\n        super.onNeighborChange(state, world, pos, neighbor);\n        if (world instanceof Level) {\n            NetworkHelpers.onElementProviderBlockNeighborChange((Level) world, pos, null);\n        }\n    }\n\n    @Override\n    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {\n        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);\n\n        if (!CableHelpers.isRemovingCable()) {\n            CableHelpers.onCableRemoved(level, pos);\n        }\n    }\n\n    protected boolean isPickBlockPersistData() {\n        return false;\n    }\n\n    @Override\n    public ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData, Player player) {\n        if (isPickBlockPersistData()) {\n            return super.getCloneItemStack(level, pos, state, includeData, player);\n        } else {\n            return getCloneItemStack(level, pos, state, false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/IgnoredBlock.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.block.Block;\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.EnumProperty;\n\n/**\n * A block that is not visible to the player.\n * Just used for providing models, until a better way for doing this comes around.\n * @author rubensworks\n */\npublic class IgnoredBlock extends Block {\n\n    public static final EnumProperty<Direction> FACING = BlockStateProperties.FACING;\n\n    public IgnoredBlock(Properties properties) {\n        super(properties);\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        builder.add(FACING);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/IgnoredBlockStatus.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport com.google.common.base.Predicates;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.StringRepresentable;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.EnumProperty;\n\nimport java.util.Locale;\n\n/**\n * A block that is not visible to the player.\n * Just used for providing models, until a better way for doing this comes around.\n * @author rubensworks\n */\npublic class IgnoredBlockStatus extends IgnoredBlock {\n\n    public static final EnumProperty<Status> STATUS = EnumProperty.create(\"status\", Status.class, Predicates.alwaysTrue());\n\n    public IgnoredBlockStatus(Properties properties) {\n        super(properties);\n\n        this.registerDefaultState(this.stateDefinition.any()\n                .setValue(FACING, Direction.NORTH)\n                .setValue(STATUS, Status.INACTIVE));\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        super.createBlockStateDefinition(builder);\n        builder.add(STATUS);\n    }\n\n    public enum Status implements StringRepresentable {\n\n        ACTIVE,\n        INACTIVE,\n        ERROR;\n\n        @Override\n        public String getSerializedName() {\n            return this.name().toLowerCase(Locale.ENGLISH);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/VoxelShapeComponents.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport com.google.common.collect.Lists;\nimport it.unimi.dsi.fastutil.doubles.DoubleArrayList;\nimport it.unimi.dsi.fastutil.doubles.DoubleList;\nimport net.minecraft.core.AxisCycle;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.ai.attributes.AttributeInstance;\nimport net.minecraft.world.entity.ai.attributes.Attributes;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.DiscreteVoxelShape;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.block.shapes.CollisionContextBlockSupport;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * A {@link VoxelShape} that contains one or more {@link VoxelShapeComponents.IComponent}.\n *\n * These components are used to handle ray tracing on seperate components.\n * Consequently, ray trace results on this object may be safely cast to {@link BlockRayTraceResultComponent}\n * so that the targeted component may be retrieved.\n *\n * @author rubensworks\n */\npublic class VoxelShapeComponents extends VoxelShape implements Iterable<VoxelShape> {\n\n    private final Collection<Pair<VoxelShape, IComponent>> entries;\n    private final String stateId;\n\n    protected VoxelShapeComponents(Collection<Pair<VoxelShape, IComponent>> entries, String stateId) {\n        super(createInnerPart(entries));\n        this.entries = entries;\n        this.stateId = stateId;\n    }\n\n    protected static DiscreteVoxelShape createInnerPart(Collection<Pair<VoxelShape, IComponent>> entries) {\n        return new Part(entries.stream()\n                .map(pair -> pair.getLeft().shape)\n                .collect(Collectors.toList()));\n    }\n\n    public static VoxelShapeComponents create(BlockState blockState, BlockGetter world, BlockPos blockPos,\n                                              CollisionContext selectionContext, List<IComponent> components) {\n        List<Pair<VoxelShape, IComponent>> entries = Lists.newArrayList();\n        for (VoxelShapeComponents.IComponent component : components) {\n            VoxelShape shape = component.getShape(blockState, world, blockPos, selectionContext);\n            entries.add(Pair.of(shape, component));\n        }\n\n        StringBuilder stateIdBuilder = new StringBuilder();\n        for (IComponent component : components) {\n            stateIdBuilder.append(component.getStateId(blockState, world, blockPos));\n            stateIdBuilder.append(\";\");\n        }\n        stateIdBuilder.append(selectionContext instanceof CollisionContextBlockSupport);\n\n        return new VoxelShapeComponents(entries, stateIdBuilder.toString());\n    }\n\n    public String getStateId() {\n        return this.stateId;\n    }\n\n    @Override\n    public Iterator<VoxelShape> iterator() {\n        return entries.stream().map(Pair::getLeft).iterator();\n    }\n\n    @Override\n    public double min(Direction.Axis axis) {\n        boolean first = true;\n        double startMin = 0;\n        for (VoxelShape shape : this) {\n            double start = shape.min(axis);\n            if (first || start < startMin) {\n                startMin = start;\n                first = false;\n            }\n        }\n        return startMin;\n    }\n\n    @Override\n    public double max(Direction.Axis axis) {\n        boolean first = true;\n        double endMax = 0;\n        for (VoxelShape shape : this) {\n            double end = shape.max(axis);\n            if (first || end > endMax) {\n                endMax = end;\n                first = false;\n            }\n        }\n        return endMax;\n    }\n\n    @Override\n    public DoubleList getCoords(Direction.Axis axis) {\n        DoubleArrayList values = new DoubleArrayList();\n        for (VoxelShape shape : this) {\n            values.addAll(shape.getCoords(axis));\n        }\n        return values;\n    }\n\n    @Override\n    public boolean isEmpty() {\n        for (VoxelShape shape : this) {\n            if (!shape.isEmpty()) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public VoxelShape move(double x, double y, double z) {\n        List<Pair<VoxelShape, IComponent>> entries = Lists.newArrayList();\n        for (Pair<VoxelShape, IComponent> entry : this.entries) {\n            entries.add(Pair.of(entry.getLeft().move(x, y, z), entry.getRight()));\n        }\n        return new VoxelShapeComponents(entries, this.stateId);\n    }\n\n    @Override\n    public void forAllEdges(Shapes.DoubleLineConsumer consumer) {\n        for (VoxelShape shape : this) {\n            shape.forAllEdges(consumer);\n        }\n    }\n\n    @Override\n    public void forAllBoxes(Shapes.DoubleLineConsumer consumer) {\n        for (VoxelShape shape : this) {\n            shape.forAllBoxes(consumer);\n        }\n    }\n\n    @Override\n    public double max(Direction.Axis axis, double a, double b) {\n        boolean first = true;\n        double valueMax = 0D;\n        for (VoxelShape shape : this) {\n            double value = shape.max(axis, a, b);\n            if (first || value > valueMax) {\n                valueMax = value;\n                first = false;\n            }\n        }\n        return valueMax;\n    }\n\n    @Nullable\n    @Override\n    public BlockRayTraceResultComponent clip(Vec3 startVec, Vec3 endVec, BlockPos pos) {\n        // Find component with shape that is closest to the startVec\n        double distanceMin = Double.POSITIVE_INFINITY;\n        VoxelShapeComponents.IComponent componentMin = null;\n        BlockHitResult resultMin = null;\n\n        for (Pair<VoxelShape, IComponent> entry : entries) {\n            VoxelShape shape = entry.getLeft();\n            BlockHitResult result = shape.clip(startVec, endVec, pos);\n            if (result != null) {\n                double distance = result.getLocation().distanceToSqr(startVec);\n                if (distance < distanceMin) {\n                    // If the previous match was a part and the current one is a facade,\n                    // check if the direction of that part matches with the matched face of the facade,\n                    // and if so, don't match the facade,\n                    // because we want the user to be able to select parts within facades.\n                    if (resultMin == null || !(entry.getRight().isRaytraceLastForFace() && componentMin.getRaytraceDirection() == result.getDirection())) {\n                        distanceMin = distance;\n                        componentMin = entry.getRight();\n                        resultMin = result;\n                    }\n                }\n            }\n        }\n\n        // Store the component in the ray trace result when we found one.\n        if (resultMin != null) {\n            return new BlockRayTraceResultComponent(resultMin, componentMin);\n        }\n\n        return null;\n    }\n\n    /**\n     * Do a ray trace for the current look direction of the player.\n     * @param pos The block position to perform a ray trace for.\n     * @param entity The entity.\n     * @return A holder object with information on the ray tracing.\n     */\n    @Nullable\n    public BlockRayTraceResultComponent rayTrace(BlockPos pos, @Nullable Entity entity) {\n        if(entity == null) {\n            return null;\n        }\n        AttributeInstance reachDistanceAttribute = entity instanceof LivingEntity ? ((LivingEntity) entity).getAttribute(Attributes.BLOCK_INTERACTION_RANGE) : null;\n        double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();\n\n        double eyeHeight = entity.level().isClientSide() ? entity.getEyeHeight() : entity.getEyeHeight(); // Client removed :  - player.getDefaultEyeHeight()\n        Vec3 lookVec = entity.getLookAngle();\n        Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());\n        Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);\n\n        return clip(origin, direction, pos);\n    }\n\n    @Override\n    public double collideX(AxisCycle rotation, AABB axisAlignedBB, double range) {\n        boolean first = true;\n        double valueBest = 0D;\n        for (VoxelShape shape : this) {\n            double value = shape.collideX(rotation, axisAlignedBB, range);\n            if (range > 0) {\n                if (first || value < valueBest) {\n                    valueBest = value;\n                    first = false;\n                }\n            } else {\n                if (first || value > valueBest) {\n                    valueBest = value;\n                    first = false;\n                }\n            }\n        }\n        return valueBest;\n    }\n\n    public static class Part extends DiscreteVoxelShape implements Iterable<DiscreteVoxelShape> {\n\n        private final Collection<DiscreteVoxelShape> entries;\n\n        public Part(Collection<DiscreteVoxelShape> entries) {\n            super(0, 0, 0);\n            this.entries = entries;\n        }\n\n        @Override\n        public Iterator<DiscreteVoxelShape> iterator() {\n            return entries.iterator();\n        }\n\n        @Override\n        public boolean isFullWide(int x, int y, int z) {\n            for (DiscreteVoxelShape part : this) {\n                if (part.isFullWide(x, y, z)) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        @Override\n        public boolean isFull(int x, int y, int z) {\n            for (DiscreteVoxelShape part : this) {\n                if (part.isFull(x, y, z)) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        @Override\n        public void fill(int x, int y, int z) {\n            for (DiscreteVoxelShape part : this) {\n                part.fill(x, y, z);\n            }\n        }\n\n        @Override\n        public int firstFull(Direction.Axis axis) {\n            boolean first = true;\n            int startMin = 0;\n            for (DiscreteVoxelShape part : this) {\n                int start = part.firstFull(axis);\n                if (first || start < startMin) {\n                    startMin = start;\n                    first = false;\n                }\n            }\n            return startMin;\n        }\n\n        @Override\n        public int lastFull(Direction.Axis axis) {\n            boolean first = true;\n            int endMax = 0;\n            for (DiscreteVoxelShape part : this) {\n                int end = part.lastFull(axis);\n                if (first || end > endMax) {\n                    endMax = end;\n                    first = false;\n                }\n            }\n            return endMax;\n        }\n\n        @Override\n        public int getSize(Direction.Axis axis) {\n            boolean first = true;\n            int sizeMax = 0;\n            for (DiscreteVoxelShape part : this) {\n                int size = part.getSize(axis);\n                if (first || size > sizeMax) {\n                    sizeMax = size;\n                    first = false;\n                }\n            }\n            return sizeMax;\n        }\n\n        @Override\n        public void forAllBoxes(IntLineConsumer consumer, boolean p_197831_2_) {\n            for (DiscreteVoxelShape part : this) {\n                part.forAllBoxes(consumer, p_197831_2_);\n            }\n        }\n    }\n\n    public static interface IComponent {\n\n        /**\n         * @param blockState The block state.\n         * @param world The world.\n         * @param blockPos The position.\n         * @return Unique identifier for the component's state.\n         */\n        public String getStateId(BlockState blockState, BlockGetter world, BlockPos blockPos);\n\n        /**\n         * Get the shape of this component.\n         * @param blockState The block state.\n         * @param world The world.\n         * @param blockPos The position.\n         * @param selectionContext The selection context.\n         * @return The shape.\n         */\n        public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext);\n\n        /**\n         * Get the pick block item.\n         * @param world The world\n         * @param pos The position\n         * @return The item.\n         */\n        public ItemStack getCloneItemStack(Level world, BlockPos pos);\n\n        /**\n         * Destroy this component\n         * @param world The world\n         * @param pos The position\n         * @param player The player destroying the component.\n         * @param saveState If the component state should be saved in the dropped item.\n         * @return If the complete block was destroyed\n         */\n        public boolean destroy(Level world, BlockPos pos, Player player, boolean saveState);\n\n        /**\n         * When this component has been activated.\n         * @param state The block state.\n         * @param world The world.\n         * @param blockPos The position.\n         * @param player The player.\n         * @param hand The hand.\n         * @param hit The ray trace result.\n         * @return Action result.\n         */\n        public InteractionResult onBlockActivated(BlockState state, Level world, BlockPos blockPos, Player player,\n                                                 InteractionHand hand, BlockRayTraceResultComponent hit);\n\n        /**\n         * @return The direction this component points at.\n         */\n        @Nullable\n        public Direction getRaytraceDirection();\n\n        /**\n         * @return If this component should only be raytraced if no other components matched for this face.\n         */\n        public boolean isRaytraceLastForFace();\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/block/VoxelShapeComponentsFactory.java",
    "content": "package org.cyclops.integrateddynamics.core.block;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.shapes.CollisionContext;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * Allows {@link VoxelShapeComponents} instances to be created\n * based on a set of registered handlers that can create components\n * under given circumstances.\n * @author rubensworks\n */\npublic class VoxelShapeComponentsFactory {\n\n    private final List<IHandler> handlers = Lists.newArrayList();\n\n    public VoxelShapeComponentsFactory(IHandler... handlers) {\n        for (IHandler handler : handlers) {\n            addHandler(handler);\n        }\n    }\n\n    public void addHandler(IHandler handler) {\n        this.handlers.add(handler);\n    }\n\n    public VoxelShapeComponents createShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext) {\n        List<VoxelShapeComponents.IComponent> components = Lists.newArrayList();\n        for (IHandler handler : handlers) {\n            components.addAll(handler.createComponents(blockState, world, blockPos, selectionContext));\n        }\n        return VoxelShapeComponents.create(blockState, world, blockPos, selectionContext, components);\n    }\n\n    public static interface IHandler {\n        public Collection<VoxelShapeComponents.IComponent> createComponents(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityActiveVariableBase.java",
    "content": "package org.cyclops.integrateddynamics.core.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.cyclopscore.persist.nbt.NBTClassType;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkEventListener;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.core.evaluate.InventoryVariableEvaluator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\n/**\n * Base part entity that can hold variables.\n * @param <E> The type of event listener\n * @author rubensworks\n */\npublic abstract class BlockEntityActiveVariableBase<E> extends BlockEntityCableConnectableInventory implements IDirtyMarkListener, INetworkEventListener<E> {\n\n    private final InventoryVariableEvaluator<IValue> evaluator;\n\n    public BlockEntityActiveVariableBase(BlockEntityType<?> type, BlockPos blockPos, BlockState blockState, int inventorySize) {\n        super(type, blockPos, blockState, inventorySize, 1);\n        getInventory().addDirtyMarkListener(this);\n\n        this.evaluator = createEvaluator();\n    }\n\n    public static class CapabilityRegistrar<T extends BlockEntityActiveVariableBase<?>> extends BlockEntityCableConnectableInventory.CapabilityRegistrar<T> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends T>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    Capabilities.ValueInterface.BLOCK,\n                    (blockEntity, context) -> () -> {\n                        INetwork network = blockEntity.getNetwork();\n                        IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n                        if (blockEntity.hasVariable()) {\n                            IVariable<?> variable = blockEntity.getVariable(partNetwork);\n                            if (variable != null) {\n                                return Optional.of(variable.getValue());\n                            }\n                        }\n                        return Optional.empty();\n                    }\n            );\n        }\n    }\n\n    protected InventoryVariableEvaluator<IValue> createEvaluator() {\n        return new InventoryVariableEvaluator<>(this.getInventory(), getSlotRead(), () -> ValueDeseralizationContext.of(getLevel()), ValueTypes.CATEGORY_ANY);\n    }\n\n    public InventoryVariableEvaluator getEvaluator() {\n        return evaluator;\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        super.saveAdditional(output);\n        List<MutableComponent> errors = evaluator.getErrors();\n        NBTClassType.writeNbt(List.class, \"errors\", errors, output);\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        evaluator.setErrors(NBTClassType.readNbt(List.class, \"errors\", input));\n        super.read(input);\n    }\n\n    public abstract int getSlotRead();\n\n    public boolean hasVariable() {\n        return !getInventory().getItem(getSlotRead()).isEmpty();\n    }\n\n    protected void updateReadVariable(boolean sendVariablesUpdateEvent) {\n        evaluator.refreshVariable(getNetwork(), sendVariablesUpdateEvent);\n        sendUpdate();\n    }\n\n    @Override\n    public void onDirty() {\n        if(!level.isClientSide()) {\n            updateReadVariable(true);\n        }\n    }\n\n    @Nullable\n    public IVariable<?> getVariable(IPartNetwork network) {\n        return evaluator.getVariable(getNetwork(), network);\n    }\n\n    @Override\n    public boolean hasEventSubscriptions() {\n        return true;\n    }\n\n    @Override\n    public Set<Class<? extends INetworkEvent>> getSubscribedEvents() {\n        return Sets.<Class<? extends INetworkEvent>>newHashSet(VariableContentsUpdatedEvent.class);\n    }\n\n    @Override\n    public void onEvent(INetworkEvent event, E networkElement) {\n        if(event instanceof VariableContentsUpdatedEvent) {\n            updateReadVariable(false);\n        }\n    }\n\n    @Override\n    public void afterNetworkReAlive() {\n        super.afterNetworkReAlive();\n        updateReadVariable(true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityCableConnectable.java",
    "content": "package org.cyclops.integrateddynamics.core.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.cyclopscore.capability.registrar.BlockEntityCapabilityRegistrar;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkCarrier;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.capability.cable.CableTile;\nimport org.cyclops.integrateddynamics.capability.network.NetworkCarrierDefault;\nimport org.cyclops.integrateddynamics.capability.path.PathElementTile;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport java.util.function.Supplier;\n\n/**\n * A part entity whose block can connect with cables.\n * @author rubensworks\n */\npublic abstract class BlockEntityCableConnectable extends CyclopsBlockEntity {\n\n    @NBTPersist\n    private EnumFacingMap<Boolean> connected = EnumFacingMap.newMap();\n\n    private final ICable cable;\n    private final INetworkCarrier networkCarrier;\n\n    public BlockEntityCableConnectable(BlockEntityType<?> type, BlockPos blockPos, BlockState blockState) {\n        super(type, blockPos, blockState);\n        cable = new CableTile<BlockEntityCableConnectable>(this) {\n\n            @Override\n            protected boolean isForceDisconnectable() {\n                return false;\n            }\n\n            @Override\n            protected EnumFacingMap<Boolean> getForceDisconnected() {\n                return null;\n            }\n\n            @Override\n            protected EnumFacingMap<Boolean> getConnected() {\n                return tile.connected;\n            }\n        };\n        networkCarrier = new NetworkCarrierDefault();\n    }\n\n    public static class CapabilityRegistrar<T extends BlockEntityCableConnectable> extends BlockEntityCapabilityRegistrar<T> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends T>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            add(\n                    Capabilities.Cable.BLOCK,\n                    (blockEntity, context) -> blockEntity.getCable()\n            );\n            add(\n                    Capabilities.NetworkCarrier.BLOCK,\n                    (blockEntity, context) -> blockEntity.getNetworkCarrier()\n            );\n            add(\n                    Capabilities.PathElement.BLOCK,\n                    (blockEntity, context) -> new PathElementTile<>(blockEntity, blockEntity.getCable())\n            );\n        }\n    }\n\n    public EnumFacingMap<Boolean> getConnected() {\n        return connected;\n    }\n\n    public ICable getCable() {\n        return cable;\n    }\n\n    public INetworkCarrier getNetworkCarrier() {\n        return networkCarrier;\n    }\n\n    public abstract INetworkElementProvider getNetworkElementProvider();\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        connected.clear();\n    }\n\n    @Override\n    public void onChunkUnloaded() {\n        super.onChunkUnloaded();\n        if (getLevel() != null && !getLevel().isClientSide()) {\n            INetwork network = getNetworkCarrier().getNetwork();\n            if (network != null) {\n                NetworkHelpers.invalidateNetworkElements(getLevel(), getBlockPos(), network, getNetworkElementProvider());\n            }\n        }\n    }\n\n    @Override\n    public void preRemoveSideEffects(BlockPos pos, BlockState state) {\n        super.preRemoveSideEffects(pos, state);\n\n        if (!CableHelpers.isRemovingCable()) {\n            CableHelpers.onCableRemoving(level, pos, false, false, state, this);\n        }\n    }\n\n    public static class Ticker<T extends BlockEntityCableConnectable> extends BlockEntityTickerDelayed<T> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, T blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.getConnected().isEmpty()) {\n                blockEntity.getCable().updateConnections();\n            }\n            NetworkHelpers.revalidateNetworkElements(level, pos);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityCableConnectableInventory.java",
    "content": "package org.cyclops.integrateddynamics.core.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.Containers;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.cyclopscore.capability.registrar.BlockEntityCapabilityRegistrar;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.SimpleInventoryState;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkCarrier;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.capability.cable.CableTile;\nimport org.cyclops.integrateddynamics.capability.network.NetworkCarrierDefault;\nimport org.cyclops.integrateddynamics.capability.path.PathElementTile;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Supplier;\n\n/**\n * A part entity with inventory whose block can connect with cables.\n * @author rubensworks\n */\npublic abstract class BlockEntityCableConnectableInventory extends CyclopsBlockEntity {\n\n    @NBTPersist\n    private EnumFacingMap<Boolean> connected = EnumFacingMap.newMap();\n\n    private final ICable cable;\n    private final INetworkCarrier networkCarrier;\n    private final SimpleInventory inventory;\n\n    public BlockEntityCableConnectableInventory(BlockEntityType<?> type, BlockPos blockPos, BlockState blockState, int inventorySize, int stackSize) {\n        super(type, blockPos, blockState);\n        inventory = createInventory(inventorySize, stackSize);\n        cable = new CableTile<BlockEntityCableConnectableInventory>(this) {\n\n            @Override\n            protected boolean isForceDisconnectable() {\n                return false;\n            }\n\n            @Override\n            protected EnumFacingMap<Boolean> getForceDisconnected() {\n                return null;\n            }\n\n            @Override\n            protected EnumFacingMap<Boolean> getConnected() {\n                return tile.connected;\n            }\n        };\n        networkCarrier = new NetworkCarrierDefault();\n    }\n\n    public static class CapabilityRegistrar<T extends BlockEntityCableConnectableInventory> extends BlockEntityCapabilityRegistrar<T> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends T>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            add(\n                    Capabilities.Cable.BLOCK,\n                    (blockEntity, context) -> blockEntity.getCable()\n            );\n            add(\n                    Capabilities.NetworkCarrier.BLOCK,\n                    (blockEntity, context) -> blockEntity.getNetworkCarrier()\n            );\n            add(\n                    Capabilities.PathElement.BLOCK,\n                    (blockEntity, context) -> new PathElementTile<>(blockEntity, blockEntity.getCable())\n            );\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, context) -> VanillaContainerWrapper.of(blockEntity.getInventory())\n            );\n            add(\n                    org.cyclops.commoncapabilities.api.capability.Capabilities.InventoryState.BLOCK,\n                    (blockEntity, context) -> new SimpleInventoryState(blockEntity.getInventory())\n            );\n        }\n    }\n\n    public EnumFacingMap<Boolean> getConnected() {\n        return connected;\n    }\n\n    public ICable getCable() {\n        return cable;\n    }\n\n    public INetworkCarrier getNetworkCarrier() {\n        return networkCarrier;\n    }\n\n    public abstract INetworkElementProvider getNetworkElementProvider();\n\n    protected SimpleInventory createInventory(int inventorySize, int stackSize) {\n        return new SimpleInventory(inventorySize, stackSize) {\n            @Override\n            public boolean stillValid(Player entityplayer) {\n                return level.getBlockEntity(worldPosition) == BlockEntityCableConnectableInventory.this;\n            }\n        };\n    }\n\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        connected.clear();\n        inventory.readFromNBT(input, \"inventory\");\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        inventory.writeToNBT(output, \"inventory\");\n        super.saveAdditional(output);\n    }\n\n    /**\n     * Called after the network has been fully initialized\n     */\n    public void afterNetworkReAlive() {\n\n    }\n\n    @Nullable\n    public INetwork getNetwork() {\n        return this.networkCarrier.getNetwork();\n    }\n\n    public SimpleInventory getInventory() {\n        return inventory;\n    }\n\n    @Override\n    public void onChunkUnloaded() {\n        super.onChunkUnloaded();\n        if (getLevel() != null && !getLevel().isClientSide()) {\n            INetwork network = getNetworkCarrier().getNetwork();\n            if (network != null) {\n                NetworkHelpers.invalidateNetworkElements(getLevel(), getBlockPos(), network, getNetworkElementProvider());\n            }\n        }\n    }\n\n    @Override\n    public void preRemoveSideEffects(BlockPos pos, BlockState state) {\n        super.preRemoveSideEffects(pos, state);\n\n        Containers.dropContents(level, pos, this.getInventory());\n        if (!CableHelpers.isRemovingCable()) {\n            CableHelpers.onCableRemoving(level, pos, false, false, state, this);\n        }\n    }\n\n    public static class Ticker<T extends BlockEntityCableConnectableInventory> extends BlockEntityTickerDelayed<T> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, T blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.getConnected().isEmpty()) {\n                blockEntity.getCable().updateConnections();\n            }\n            if (NetworkHelpers.revalidateNetworkElements(level, pos)) {\n                blockEntity.afterNetworkReAlive();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityMechanicalMachine.java",
    "content": "package org.cyclops.integrateddynamics.core.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.Recipe;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;\nimport net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.datastructure.SingleCache;\nimport org.cyclops.cyclopscore.inventory.InventorySlotMasked;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.network.IEnergyNetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.network.MechanicalMachineNetworkElement;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * An abstract machine base tile entity that is able to process recipes by consuming energy.\n * @param <RCK> The recipe cache key type.\n * @param <R> The recipe type.\n */\npublic abstract class BlockEntityMechanicalMachine<RCK, R extends Recipe<?>> extends BlockEntityCableConnectableInventory {\n\n    /**\n     * The number of ticks to sleep when the recipe could not be finalized.\n     */\n    private static int SLEEP_TIME = 40;\n\n    private final SimpleEnergyHandler energyHandler;\n\n    @NBTPersist\n    private int progress = -1;\n    @NBTPersist\n    private int sleep = -1;\n\n    private SingleCache<RCK, Optional<RecipeHolder<R>>> recipeCache;\n\n    public BlockEntityMechanicalMachine(BlockEntityType<?> type, BlockPos blockPos, BlockState blockState, int inventorySize) {\n        super(type, blockPos, blockState, inventorySize, 64);\n\n        this.energyHandler = new SimpleEnergyHandler(getMaxEnergyStored(), getMaxEnergyStored(), 0, 0) {\n            @Override\n            protected void onEnergyChanged(int previousAmount) {\n                super.onEnergyChanged(previousAmount);\n                setChanged();\n            }\n        };\n\n        // Efficient cache to retrieve the current craftable recipe.\n        recipeCache = new SingleCache<>(createCacheUpdater());\n    }\n\n    public static class CapabilityRegistrar<T extends BlockEntityMechanicalMachine<?, ?>> extends BlockEntityCableConnectableInventory.CapabilityRegistrar<T> {\n        public CapabilityRegistrar(Supplier<BlockEntityType<? extends T>> blockEntityType) {\n            super(blockEntityType);\n        }\n\n        @Override\n        public void populate() {\n            super.populate();\n\n            add(\n                    Capabilities.NetworkElementProvider.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getNetworkElementProvider()\n            );\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK,\n                    (blockEntity, direction) -> blockEntity.getEnergyHandler()\n            );\n            add(\n                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,\n                    (blockEntity, direction) -> {\n                        int[] slots = direction == Direction.DOWN ? blockEntity.getOutputSlots() : blockEntity.getInputSlots();\n                        return VanillaContainerWrapper.of(new InventorySlotMasked(blockEntity.getInventory(), slots));\n                    }\n            );\n        }\n    }\n\n    @Override\n    public INetworkElementProvider getNetworkElementProvider() {\n        return new NetworkElementProviderSingleton() {\n            @Override\n            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {\n                return new MechanicalMachineNetworkElement(DimPos.of(world, blockPos));\n            }\n        };\n    }\n\n    public SimpleEnergyHandler getEnergyHandler() {\n        return energyHandler;\n    }\n\n    /**\n     * @return A new cache updater instance.\n     */\n    protected abstract SingleCache.ICacheUpdater<RCK, Optional<RecipeHolder<R>>> createCacheUpdater();\n\n    /**\n     * @return The available input slots.\n     */\n    public abstract int[] getInputSlots();\n\n    /**\n     * @return The available output slots.\n     */\n    public abstract int[] getOutputSlots();\n\n    /**\n     * @return If the machine was in a working state.\n     */\n    public abstract boolean wasWorking();\n\n    /**\n     * Set the new working state.\n     * @param working If the machine is working.\n     */\n    public abstract void setWorking(boolean working);\n\n    /**\n     * @return If the machine currently has any work to process.\n     */\n    public boolean hasWork() {\n        return getCurrentRecipe() != null;\n    }\n\n    /**\n     * @return If the machine is currently working.\n     */\n    public boolean isWorking() {\n        return this.progress >= 0 && this.sleep == -1;\n    }\n\n    /**\n     * @return If the machine is able to work in its current state.\n     *         This for example takes into account the available energy.\n     */\n    public boolean canWork() {\n        int rate = getEnergyConsumptionRate();\n        return drainEnergy(rate, true) == rate && !level.hasNeighborSignal(getBlockPos());\n    }\n\n    /**\n     * @return If the machine is currently sleeping due to a recipe that could not be finalized.\n     */\n    public boolean isSleeping() {\n        return this.sleep > 0;\n    }\n\n    public void setProgress(int progress) {\n        this.progress = progress;\n    }\n\n    public void setSleep(int sleep) {\n        this.sleep = sleep;\n    }\n\n    public int getSleep() {\n        return sleep;\n    }\n\n    public Optional<IEnergyNetwork> getEnergyNetwork() {\n        return NetworkHelpers.getEnergyNetwork(getNetwork());\n    }\n\n    public void onTankChanged() {\n        setChanged();\n        getInventory().setChanged();\n    }\n\n    @Override\n    protected SimpleInventory createInventory(int inventorySize, int stackSize) {\n        return new SimpleInventory(inventorySize, stackSize) {\n            @Override\n            public boolean canPlaceItem(int i, ItemStack itemstack) {\n                return ArrayUtils.contains(getInputSlots(), i) && super.canPlaceItem(i, itemstack);\n            }\n\n            @Override\n            protected void onInventoryChanged() {\n                super.onInventoryChanged();\n                BlockEntityMechanicalMachine.this.sleep = -1;\n            }\n        };\n    }\n\n    /**\n     * @return The recipe registry this machine should work with..\n     */\n    protected abstract RecipeType<? extends R> getRecipeRegistry();\n\n    /**\n     * @return The current recipe cache key that is used to determine the current input of a recipe.\n     */\n    protected abstract RCK getCurrentRecipeCacheKey();\n\n    /**\n     * @return The currently applicable recipe.\n     */\n    public Optional<RecipeHolder<R>> getCurrentRecipe() {\n        return recipeCache.get(getCurrentRecipeCacheKey());\n    }\n\n    /**\n     * @return The current recipe progress, going from 0 to maxProgress.\n     */\n    public int getProgress() {\n        return progress;\n    }\n\n    /**\n     * @return The current maximum progress.\n     */\n    public int getMaxProgress() {\n        return this.getCurrentRecipe()\n                .map(this::getRecipeDuration)\n                .orElse(0);\n    }\n\n    /**\n     * @param recipe A recipe.\n     * @return The duration of a given recipe.\n     */\n    public abstract int getRecipeDuration(RecipeHolder<R> recipe);\n\n    /**\n     * Finalize a recipe.\n     * This should insert the recipe output in the machine, and consume the input.\n     * If the output could not be added, this method should return false.\n     * @param recipe A recipe.\n     * @param simulate If finalization should be simulated.\n     * @return If finalization was successful.\n     */\n    protected abstract boolean finalizeRecipe(R recipe, boolean simulate);\n\n    /**\n     * Update the working state.\n     */\n    public void updateWorkingState() {\n        boolean wasWorking = wasWorking();\n        boolean isWorking = isWorking();\n        if (isWorking != wasWorking) {\n            setWorking(isWorking);\n        }\n    }\n\n    /**\n     * @return The energy consumption rate per (working) tick.\n     */\n    public abstract int getEnergyConsumptionRate();\n\n    /**\n     * Drain energy from the internal buffer or the attached network.\n     * @param amount The amount of energy.\n     * @param simulate If drainage should be simulated.\n     * @return The drained energy.\n     */\n    protected int drainEnergy(int amount, boolean simulate) {\n        int toDrain = amount;\n\n        // First, check internal buffer\n        toDrain -= this.extractEnergyInternal(toDrain, simulate);\n\n        if (toDrain > 0) {\n            // If we still need energy, ask it from the network.\n            IEnergyNetwork energyNetwork = getEnergyNetwork().orElse(null);\n            if (energyNetwork != null) {\n                toDrain -= energyNetwork.getChannel(IPositionedAddonsNetwork.DEFAULT_CHANNEL).extract(toDrain, simulate);\n            }\n        }\n        return amount - toDrain;\n    }\n\n    protected int extractEnergyInternal(int energy, boolean simulate) {\n        energy = Math.max(0, energy);\n        int stored = getEnergyHandler().getAmountAsInt();\n        int newEnergy = Math.max(stored - energy, 0);\n        if(!simulate) {\n            getEnergyHandler().set(newEnergy);\n        }\n        return stored - newEnergy;\n    }\n\n    protected abstract int getMaxEnergyStored();\n\n    @Override\n    public void read(ValueInput input) {\n        super.read(input);\n        energyHandler.deserialize(input);\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        super.saveAdditional(output);\n        energyHandler.serialize(output);\n    }\n\n    public static class Ticker<RCK, R extends Recipe<?>, BE extends BlockEntityMechanicalMachine<RCK, R>> extends BlockEntityCableConnectableInventory.Ticker<BE> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, BE blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.isSleeping()) {\n                blockEntity.setSleep(blockEntity.getSleep() - 1);\n                blockEntity.setChanged();\n            } else if (blockEntity.canWork()) {\n                Optional<RecipeHolder<R>> recipeOptional = blockEntity.getCurrentRecipe();\n                if (recipeOptional.isPresent()) {\n                    RecipeHolder<R> recipe = recipeOptional.get();\n                    if (blockEntity.getProgress() == 0 && !blockEntity.finalizeRecipe(recipe.value(), true)) {\n                        blockEntity.setSleep(SLEEP_TIME);\n                    } else if (blockEntity.getProgress() < blockEntity.getMaxProgress()) {\n                        // // Consume energy while progressing\n                        int toDrain = blockEntity.getEnergyConsumptionRate();\n                        if (blockEntity.drainEnergy(toDrain, true) == toDrain) {\n                            blockEntity.drainEnergy(toDrain, false);\n                            blockEntity.setProgress(blockEntity.getProgress() + 1);\n                            blockEntity.setSleep(-1);\n                        } else {\n                            blockEntity.setSleep(1);\n                        }\n                    } else {\n                        // Otherwise, finish and output\n\n                        // First check if we have enough room for the recipe output,\n                        // if not, we sleep for a while.\n                        if (blockEntity.finalizeRecipe(recipe.value(), true)) {\n                            blockEntity.setProgress(0);\n                            blockEntity.finalizeRecipe(recipe.value(), false);\n                        } else {\n                            blockEntity.setSleep(40);\n                        }\n                    }\n                } else {\n                    blockEntity.setProgress(-1);\n                    blockEntity.setSleep(-1);\n                }\n            }\n\n            // Check if a state update is needed.\n            blockEntity.updateWorkingState();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityMultipartTicking.java",
    "content": "package org.cyclops.integrateddynamics.core.blockentity;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.Connection;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.fml.ModLoader;\nimport net.neoforged.neoforge.capabilities.BlockCapability;\nimport net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;\nimport net.neoforged.neoforge.model.data.ModelData;\nimport org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;\nimport org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.datastructure.EnumFacingMap;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.persist.nbt.NBTPersist;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.IFacadeable;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkCarrier;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartCapability;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.block.BlockCable;\nimport org.cyclops.integrateddynamics.capability.cable.CableFakeableMultipartTicking;\nimport org.cyclops.integrateddynamics.capability.cable.CableTileMultipartTicking;\nimport org.cyclops.integrateddynamics.capability.dynamiclight.DynamicLightTileMultipartTicking;\nimport org.cyclops.integrateddynamics.capability.dynamicredstone.DynamicRedstoneTileMultipartTicking;\nimport org.cyclops.integrateddynamics.capability.facadeable.FacadeableDefault;\nimport org.cyclops.integrateddynamics.capability.facadeable.FacadeableTileMultipartTicking;\nimport org.cyclops.integrateddynamics.capability.network.NetworkCarrierDefault;\nimport org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderPartContainer;\nimport org.cyclops.integrateddynamics.capability.partcontainer.PartContainerTileMultipartTicking;\nimport org.cyclops.integrateddynamics.capability.path.PathElementTileMultipartTicking;\nimport org.cyclops.integrateddynamics.client.model.CableRenderState;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.event.RegisterPartCapabilitiesEvent;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * A ticking part entity which is made up of different parts.\n * @author Ruben Taelman\n */\npublic class BlockEntityMultipartTicking extends CyclopsBlockEntity implements PartHelpers.IPartStateHolderCallback {\n\n    @NBTPersist private EnumFacingMap<Boolean> connected = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Boolean> forceDisconnected = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Integer> redstoneLevels = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Boolean> redstoneInputs = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Boolean> redstoneStrong = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Integer> lastRedstonePulses = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Integer> scheduledPulseRemaining = EnumFacingMap.newMap();\n    @NBTPersist private EnumFacingMap<Integer> lightLevels = EnumFacingMap.newMap();\n    private EnumFacingMap<Integer> previousLightLevels;\n    @NBTPersist private CompoundTag facadeBlockTag = null;\n\n    private final PartContainerTileMultipartTicking partContainer;\n    private final CableTileMultipartTicking cable;\n    private final INetworkCarrier networkCarrier;\n    private final ICableFakeable cableFakeable;\n    @NBTPersist\n    private boolean forceLightCheckAtClient;\n\n    private ModelData cachedState = null;\n\n    public EnumFacingMap<Boolean> getConnected() {\n        return connected;\n    }\n\n    public EnumFacingMap<Integer> getRedstoneLevels() {\n        return redstoneLevels;\n    }\n\n    public EnumFacingMap<Boolean> getRedstoneInputs() {\n        return redstoneInputs;\n    }\n\n    public EnumFacingMap<Boolean> getRedstoneStrong() {\n        return redstoneStrong;\n    }\n\n    public EnumFacingMap<Integer> getLastRedstonePulses() {\n        return lastRedstonePulses;\n    }\n\n    public EnumFacingMap<Integer> getScheduledPulseRemaining() {\n        return scheduledPulseRemaining;\n    }\n\n    public EnumFacingMap<Integer> getLightLevels() {\n        return lightLevels;\n    }\n\n    public CompoundTag getFacadeBlockTag() {\n        return facadeBlockTag;\n    }\n\n    public void setFacadeBlockTag(CompoundTag facadeBlockTag) {\n        this.facadeBlockTag = facadeBlockTag;\n    }\n\n    public PartContainerTileMultipartTicking getPartContainer() {\n        return partContainer;\n    }\n\n    public CableTileMultipartTicking getCable() {\n        return cable;\n    }\n\n    public INetworkCarrier getNetworkCarrier() {\n        return networkCarrier;\n    }\n\n    public ICableFakeable getCableFakeable() {\n        return cableFakeable;\n    }\n\n    public void setForceLightCheckAtClient(boolean forceLightCheckAtClient) {\n        this.forceLightCheckAtClient = forceLightCheckAtClient;\n    }\n\n    public BlockEntityMultipartTicking(BlockPos blockPos, BlockState blockState) {\n        super(RegistryEntries.BLOCK_ENTITY_MULTIPART_TICKING.get(), blockPos, blockState);\n        partContainer = new PartContainerTileMultipartTicking(this);\n        cable = new CableTileMultipartTicking(this);\n        networkCarrier = new NetworkCarrierDefault();\n        cableFakeable = new CableFakeableMultipartTicking(this);\n    }\n\n    public static void registerMultipartTickingCapabilities(RegisterCapabilitiesEvent event, BlockEntityType<? extends BlockEntityMultipartTicking> blockEntityType) {\n        event.registerBlockEntity(\n                Capabilities.PartContainer.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> blockEntity.getPartContainer()\n        );\n        event.registerBlockEntity(\n                Capabilities.NetworkElementProvider.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> new NetworkElementProviderPartContainer(blockEntity.getPartContainer())\n        );\n        event.registerBlockEntity(\n                Capabilities.Facadeable.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> new FacadeableTileMultipartTicking(blockEntity)\n        );\n        event.registerBlockEntity(\n                Capabilities.Cable.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> blockEntity.getCable()\n        );\n        event.registerBlockEntity(\n                Capabilities.NetworkCarrier.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> blockEntity.getNetworkCarrier()\n        );\n        event.registerBlockEntity(\n                Capabilities.CableFakeable.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> blockEntity.getCableFakeable()\n        );\n        event.registerBlockEntity(\n                Capabilities.PathElement.BLOCK,\n                blockEntityType,\n                (blockEntity, context) -> new PathElementTileMultipartTicking(blockEntity, blockEntity.getCable())\n        );\n        registerPartCapabilityAsBlockCapability(event, blockEntityType, Capabilities.ValueInterface.BLOCK, Capabilities.ValueInterface.PART);\n        registerPartCapabilityAsBlockCapability(event, blockEntityType, Capabilities.VariableContainer.BLOCK, Capabilities.VariableContainer.PART);\n        ModLoader.postEventWrapContainerInModOrder(new RegisterPartCapabilitiesEvent(event, blockEntityType));\n\n        event.registerBlockEntity(\n                Capabilities.DynamicLight.BLOCK,\n                blockEntityType,\n                DynamicLightTileMultipartTicking::new\n        );\n        event.registerBlockEntity(\n                Capabilities.DynamicRedstone.BLOCK,\n                blockEntityType,\n                DynamicRedstoneTileMultipartTicking::new\n        );\n    }\n\n    public static <T> void registerPartCapabilityAsBlockCapability(RegisterCapabilitiesEvent event, BlockEntityType<? extends BlockEntityMultipartTicking> blockEntityType, BlockCapability<T, Direction> blockCapability, PartCapability<T> partCapability) {\n        event.registerBlockEntity(\n                blockCapability,\n                blockEntityType,\n                (blockEntity, context) -> {\n                    INetwork network = blockEntity.getNetwork();\n                    if (network != null) {\n                        IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n                        return blockEntity.getPartContainer()\n                                .getCapability(partCapability, network, partNetwork, PartTarget.fromCenter(PartPos.of(blockEntity.getLevel(), blockEntity.getBlockPos(), context)))\n                                .orElse(null);\n                    }\n                    return null;\n                }\n        );\n    }\n\n    @Override\n    public void saveAdditional(ValueOutput output) {\n        super.saveAdditional(output);\n        forceLightCheckAtClient = false;\n        partContainer.toValueOutput(output.child(\"partContainer\"));\n        output.putBoolean(\"realCable\", cableFakeable.isRealCable());\n    }\n\n    @Override\n    public void read(ValueInput input) {\n        EnumFacingMap<Boolean> lastConnected = EnumFacingMap.newMap(connected);\n        Tag lastFacadeBlock = facadeBlockTag;\n        boolean lastRealCable = cableFakeable.isRealCable();\n        partContainer.fromValueInput(input.child(\"partContainer\").orElseThrow());\n        boolean wasLightTransparent = getLevel() != null && CableHelpers.isLightTransparent(getLevel(), getBlockPos(), null, getBlockState());\n\n        super.read(input);\n        cableFakeable.setRealCable(input.getBooleanOr(\"realCable\", false));\n        boolean isLightTransparent = getLevel() != null && CableHelpers.isLightTransparent(getLevel(), getBlockPos(), null, getBlockState());\n        if (getLevel() != null && (lastConnected == null || connected == null || !lastConnected.equals(connected)\n                || !Objects.equals(lastFacadeBlock, facadeBlockTag)\n                || lastRealCable != cableFakeable.isRealCable() || wasLightTransparent != isLightTransparent)) {\n            IModHelpers.get().getBlockHelpers().markForUpdate(getLevel(), getBlockPos());\n        }\n    }\n\n    @Override\n    public void onDataPacket(Connection net, ValueInput valueInput) {\n        super.onDataPacket(net, valueInput);\n        onUpdateReceived();\n    }\n\n    protected void onUpdateReceived() {\n        if(!lightLevels.equals(previousLightLevels)) {\n            previousLightLevels = lightLevels;\n        }\n        cachedState = null;\n        IModHelpers.get().getBlockHelpers().markForUpdate(getLevel(), getBlockPos());\n        if (forceLightCheckAtClient) {\n            getLevel().getLightEngine().checkBlock(getBlockPos());\n        }\n    }\n\n    public ModelData getConnectionState() {\n        if (cachedState != null) {\n            return cachedState;\n        }\n        ModelData.Builder builder = ModelData.builder();\n        if (partContainer.getPartData() != null) { // Can be null in rare cases where rendering happens before data sync\n            builder.with(BlockCable.REALCABLE, cableFakeable.isRealCable());\n            if (connected.isEmpty()) {\n                getCable().updateConnections(false);\n            }\n            for (Direction side : Direction.values()) {\n                builder.with(BlockCable.CONNECTED[side.ordinal()],\n                        !cable.isForceDisconnected(side) && connected.get(side));\n                builder.with(BlockCable.PART_RENDERPOSITIONS[side.ordinal()],\n                        partContainer.hasPart(side) ? partContainer.getPart(side).getPartRenderPosition() : PartRenderPosition.NONE);\n            }\n            IFacadeable facadeable = Optional.ofNullable(level.getCapability(Capabilities.Facadeable.BLOCK, getBlockPos(), getBlockState(), this, null))\n                    .orElseGet(FacadeableDefault::new);\n            builder.with(BlockCable.FACADE, facadeable.hasFacade() ? Optional.of(facadeable.getFacade()) : Optional.empty());\n            builder.with(BlockCable.PARTCONTAINER, partContainer);\n            builder.with(BlockCable.RENDERSTATE, new CableRenderState(\n                    this.cableFakeable.isRealCable(),\n                    EnumFacingMap.newMap(this.connected),\n                    EnumFacingMap.newMap(this.partContainer.getPartData()),\n                    facadeBlockTag\n                    ));\n        }\n        return cachedState = builder.build();\n    }\n\n    public void updateRedstoneInfo(Direction side, boolean strongPower) {\n        this.setChanged();\n        if (getLevel().isLoaded(getBlockPos().relative(side))) {\n            Orientation orientation = ExperimentalRedstoneUtils.initialOrientation(level, side.getOpposite(), null);\n            getLevel().neighborChanged(getBlockPos().relative(side), getBlockState().getBlock(), orientation);\n            if (strongPower) {\n                // When we are emitting a strong power, also update all neighbours of the target\n                getLevel().updateNeighborsAt(getBlockPos().relative(side), getBlockState().getBlock());\n            }\n        }\n    }\n\n    public void updateLightInfo() {\n        sendUpdate();\n    }\n\n    public void updateScheduledPulses() {\n        if (!getLevel().isClientSide()) {\n            EnumFacingMap<Integer> scheduledPulses = getScheduledPulseRemaining();\n            if (!scheduledPulses.isEmpty()) {\n                java.util.List<Direction> toRemove = new java.util.ArrayList<>();\n                for (Map.Entry<Direction, Integer> entry : scheduledPulses.entrySet()) {\n                    Direction side = entry.getKey();\n                    int remaining = entry.getValue();\n                    if (remaining > 0) {\n                        remaining--;\n                        if (remaining == 0) {\n                            // Mark for removal and reset the redstone level to 0\n                            toRemove.add(side);\n                            EnumFacingMap<Integer> redstoneLevels = getRedstoneLevels();\n                            EnumFacingMap<Boolean> redstoneStrongs = getRedstoneStrong();\n                            boolean strongPower = redstoneStrongs.getOrDefault(side, false);\n                            redstoneLevels.put(side, 0);\n                            updateRedstoneInfo(side, strongPower);\n                        } else {\n                            scheduledPulses.put(side, remaining);\n                        }\n                    }\n                }\n                // Remove completed pulses\n                for (Direction side : toRemove) {\n                    scheduledPulses.remove(side);\n                }\n            }\n        }\n    }\n\n    public INetwork getNetwork() {\n        return networkCarrier.getNetwork();\n    }\n\n    @Override\n    public void onSet(PartHelpers.PartStateHolder<?, ?> partStateHolder) {\n\n    }\n\n    /**\n     * @return The raw force disconnection data.\n     */\n    public EnumFacingMap<Boolean> getForceDisconnected() {\n        return this.forceDisconnected;\n    }\n\n    public void setForceDisconnected(EnumFacingMap<Boolean> forceDisconnected) {\n        this.forceDisconnected.clear();\n        this.forceDisconnected.putAll(forceDisconnected);\n    }\n\n    @Override\n    public void onChunkUnloaded() {\n        super.onChunkUnloaded();\n        invalidateParts();\n    }\n\n    protected void invalidateParts() {\n        if (getLevel() != null && !getLevel().isClientSide()) {\n            INetwork network = getNetwork();\n            if (network != null) {\n                for (Map.Entry<Direction, PartHelpers.PartStateHolder<?, ?>> entry : partContainer.getPartData().entrySet()) {\n                    INetworkElement element = entry.getValue().getPart().createNetworkElement(getPartContainer(), DimPos.of(getLevel(), getBlockPos()), entry.getKey());\n                    element.invalidate(network);\n                }\n            }\n        }\n    }\n\n    @Override\n    protected Direction transformFacingForRotation(Direction facing) {\n        // Ignore rotations on this tile\n        return facing;\n    }\n\n    @Override\n    public void preRemoveSideEffects(BlockPos pos, BlockState state) {\n        super.preRemoveSideEffects(pos, state);\n\n        if (!CableHelpers.isRemovingCable()) {\n            CableHelpers.onCableRemoving(level, pos, false, false, state, this);\n        }\n    }\n\n    @Override\n    public void setRemoved() {\n        // Only required for cases where cables are moved by commands or cleared after game tests.\n        if (getNetworkCarrier().getNetwork() != null) {\n            CableHelpers.onCableRemovingNetwork(getBlockState(), this, getNetworkCarrier(), new PathElementTileMultipartTicking(this, this.getCable()));\n        }\n\n        super.setRemoved();\n    }\n\n    public static class Ticker<T extends BlockEntityMultipartTicking> extends BlockEntityTickerDelayed<T> {\n        @Override\n        protected void update(Level level, BlockPos pos, BlockState blockState, T blockEntity) {\n            super.update(level, pos, blockState, blockEntity);\n\n            if (blockEntity.getConnected().isEmpty()) {\n                blockEntity.getCable().updateConnections();\n            }\n\n            // Handle scheduled pulse resets before aspect evaluation\n            blockEntity.updateScheduledPulses();\n\n            blockEntity.getPartContainer().update();\n\n            // Revalidate network if that hasn't happened yet\n            if (blockEntity.getNetwork() == null) {\n                NetworkHelpers.revalidateNetworkElements(level, pos);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityMultipartTickingConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.blockentity;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockEntityConfigCommon;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMultipartTickingConfigClient;\n\n/**\n * Config for the {@link BlockEntityMultipartTicking}.\n * @author rubensworks\n *\n */\npublic class BlockEntityMultipartTickingConfig extends BlockEntityConfigCommon<BlockEntityMultipartTicking, IntegratedDynamics> {\n\n    public BlockEntityMultipartTickingConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"multipart_ticking\",\n                (eConfig) -> new BlockEntityType<>(BlockEntityMultipartTicking::new,\n                        Sets.newHashSet(RegistryEntries.BLOCK_CABLE.get()))\n        );\n        IntegratedDynamics._instance.getModEventBus().addListener(this::registerCapability);\n    }\n\n    protected void registerCapability(RegisterCapabilitiesEvent event) {\n        BlockEntityMultipartTicking.registerMultipartTickingCapabilities(event, getInstance());\n    }\n\n    @Override\n    public void onRegistered() {\n        super.onRegistered();\n        if(IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            new BlockEntityMultipartTickingConfigClient().onRegistered(this);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/ContainerScreenActiveVariableBase.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.core.client.gui.container.DisplayErrorsComponent;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerActiveVariableBase;\n\n/**\n * Base gui for part entities that can hold variables.\n * @author rubensworks\n */\npublic abstract class ContainerScreenActiveVariableBase<C extends ContainerActiveVariableBase<?>> extends ContainerScreenExtended<C> {\n\n    protected final DisplayErrorsComponent displayErrors = new DisplayErrorsComponent();\n\n    public ContainerScreenActiveVariableBase(C container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title);\n    }\n\n    protected abstract int getErrorX();\n    protected abstract int getErrorY();\n\n    protected int getValueX() {\n        return 54;\n    }\n\n    protected int getValueY() {\n        return 57;\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int x, int y, float f) {\n        super.extractBackground(guiGraphics, x, y, f);\n\n        Component readValue = getMenu().getReadValue();\n        int readValueColor = getMenu().getReadValueColor();\n        boolean ok = false;\n        if (readValue != null) {\n            ok = true;\n            IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, readValue.getString(),\n                    getGuiLeftTotal() + getValueX(), getGuiTopTotal() + getValueY(), 70, ARGB.opaque(readValueColor), false, Font.DisplayMode.NORMAL);\n        }\n\n        displayErrors.drawBackground(guiGraphics, getMenu().getReadErrors(), getErrorX(), getErrorY(), getErrorX(), getErrorY(), this,\n                this.leftPos, this.topPos, ok);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, mouseX, mouseY);\n        guiGraphics.text(font, this.title, this.titleLabelX, this.titleLabelY, ARGB.opaque(4210752), false);\n        displayErrors.drawForeground(guiGraphics, getMenu().getReadErrors(), getErrorX(), getErrorY(), mouseX, mouseY, this, this.leftPos, this.topPos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/ContainerScreenMechanicalMachine.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.item.DamageIndicatedItemComponent;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMechanicalMachine;\n\nimport java.util.Optional;\n\n/**\n * Base class for mechanical machine guis.\n * @author rubensworks\n */\npublic abstract class ContainerScreenMechanicalMachine<C extends ContainerMechanicalMachine<?>> extends ContainerScreenExtended<C> {\n\n    public ContainerScreenMechanicalMachine(C container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, title);\n    }\n\n    public void drawEnergyBarTooltip(GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int mouseX, int mouseY) {\n        IModHelpers.get().getGuiHelpers().renderTooltipOptional(this, guiGraphics, 8, 16, 18, 60, mouseX, mouseY, () -> {\n            int energyStored = getMenu().getEnergy();\n            int energyMax = getMenu().getMaxEnergy();\n            if (energyMax > 0) {\n                return Optional.of(Lists.newArrayList(\n                        Component.translatable(\"general.integrateddynamics.energy\"),\n                        Helpers.getLocalizedEnergyLevel(energyStored, energyMax)));\n            }\n            return Optional.empty();\n        });\n    }\n\n    public void drawFluidTankTooltip(GuiGraphicsExtractor guiGraphics, FluidStack fluidStack, int fluidCapacity, int x, int y, int width, int height, int mouseX, int mouseY) {\n        IModHelpers.get().getGuiHelpers().renderTooltipOptional(this, guiGraphics, x, y, width, height, mouseX, mouseY, () -> {\n            if (fluidStack != null && !fluidStack.isEmpty()) {\n                Component fluidName = fluidStack.getHoverName();\n                return Optional.of(Lists.newArrayList(fluidName,\n                        DamageIndicatedItemComponent.getInfo(fluidStack, fluidStack.getAmount(), fluidCapacity)));\n            }\n            return Optional.empty();\n        });\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int x, int y) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, x, y);\n        guiGraphics.text(font, this.title, this.titleLabelX, this.titleLabelY, ARGB.opaque(4210752), false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/IDropdownEntry.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui;\n\nimport net.minecraft.network.chat.MutableComponent;\n\nimport java.util.List;\n\n/**\n * Entry for {@link WidgetTextFieldDropdown}.\n * @author rubensworks\n */\npublic interface IDropdownEntry<V> {\n    public String getMatchString();\n    public MutableComponent getDisplayString();\n    public List<MutableComponent> getTooltip();\n    public V getValue();\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/IDropdownEntryListener.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui;\n\nimport javax.annotation.Nullable;\n\n/**\n * Listener for {@link WidgetTextFieldDropdown} dropdown element selection.\n * @param <T> The dropdown entry type.\n * @author rubensworks\n */\npublic interface IDropdownEntryListener<T> {\n    /**\n     * Will be called when a dropdown entry is selected or completely matches.\n     * @param dropdownEntry The selected entry.\n     */\n    public void onSetDropdownPossiblity(@Nullable IDropdownEntry<T> dropdownEntry);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/WidgetTextFieldDropdown.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.util.FormattedCharSequence;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.lwjgl.glfw.GLFW;\n\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * A text field that can show a dropdown for autocomplete.\n * @param <T> The dropdown entry type.\n * @author rubensworks\n */\npublic class WidgetTextFieldDropdown<T> extends WidgetTextFieldExtended {\n\n    private Set<IDropdownEntry<T>> possibilities;\n    private List<IDropdownEntry<T>> visiblePossibilities = Collections.emptyList();\n    private int visiblePossibilitiesIndex = -1;\n    private IDropdownEntry<T> selectedDropdownPossibility = null;\n    private int dropdownSize = 5;\n    private IDropdownEntryListener<T> dropdownEntryListener;\n\n    private int enabledColor = ARGB.opaque(14737632);\n    private int disabledColor = ARGB.opaque(7368816);\n\n    public IDropdownEntry<T> getSelectedDropdownPossibility() {\n        return selectedDropdownPossibility;\n    }\n\n    public int getDropdownSize() {\n        return dropdownSize;\n    }\n\n    public void setDropdownSize(int dropdownSize) {\n        this.dropdownSize = dropdownSize;\n    }\n\n    public IDropdownEntryListener<T> getDropdownEntryListener() {\n        return dropdownEntryListener;\n    }\n\n    public void setDropdownEntryListener(IDropdownEntryListener<T> dropdownEntryListener) {\n        this.dropdownEntryListener = dropdownEntryListener;\n    }\n\n    public WidgetTextFieldDropdown(Font fontrenderer, int x, int y, int width, int height,\n                                   Component narrationMessage, boolean background, Set<IDropdownEntry<T>> possibilities) {\n        super(fontrenderer, x, y, width, height, narrationMessage, background);\n        setPossibilities(Objects.requireNonNull(possibilities));\n    }\n\n    public WidgetTextFieldDropdown(Font fontrenderer, int x, int y, int width, int height,\n                                   Component narrationMessage, boolean background) {\n        this(fontrenderer, x, y, width, height, narrationMessage, background, Collections.emptySet());\n    }\n\n    public void setPossibilities(Set<IDropdownEntry<T>> possibilities) {\n        this.possibilities = possibilities;\n        this.visiblePossibilities = Collections.emptyList();\n    }\n\n    public int getPossibilitiesCount() {\n        return possibilities.size();\n    }\n\n    @Nullable\n    public IDropdownEntry<T> getVisiblePossibility(int index) {\n        return visiblePossibilities.get(index);\n    }\n\n    public void refreshDropdownList() {\n        // Remove all colors and formatting when changing text\n        if(getValue().contains(\"§\")) {\n            setValue(getValue().replaceAll(\"§.\", \"\"));\n        }\n        if (!possibilities.isEmpty()) {\n            visiblePossibilities = Lists.newArrayList();\n            for (IDropdownEntry<T> possibility : possibilities) {\n                if (possibility.getMatchString().toLowerCase().contains(getValue().toLowerCase())) {\n                    visiblePossibilities.add(possibility);\n                }\n            }\n            visiblePossibilitiesIndex = -1;\n            if (!visiblePossibilities.isEmpty()) {\n                selectedDropdownPossibility = visiblePossibilities.stream()\n                        .filter(e -> e.getMatchString().equals(getValue()))\n                        .findFirst()\n                        .orElse(null);\n            }\n            if (dropdownEntryListener != null) {\n                dropdownEntryListener.onSetDropdownPossiblity(selectedDropdownPossibility);\n            }\n        }\n    }\n    @Override\n    public void setFocused(boolean isFocusedIn) {\n        super.setFocused(isFocusedIn);\n        if (isFocusedIn) {\n            refreshDropdownList();\n        }\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (super.charTyped(evt)) {\n            refreshDropdownList();\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        IDropdownEntry<T> oldPossibility = selectedDropdownPossibility;\n        selectedDropdownPossibility = null;\n        if (!possibilities.isEmpty()) {\n            switch (evt.key()) {\n                case GLFW.GLFW_KEY_UP:\n                    if (visiblePossibilitiesIndex >= 0) {\n                        visiblePossibilitiesIndex--;\n                    } else {\n                        visiblePossibilitiesIndex = visiblePossibilities.size() - 1;\n                    }\n                    return true;\n                case GLFW.GLFW_KEY_TAB:\n                case GLFW.GLFW_KEY_DOWN:\n                    if (visiblePossibilitiesIndex < visiblePossibilities.size() - 1) {\n                        visiblePossibilitiesIndex++;\n                    } else {\n                        visiblePossibilitiesIndex = 0;\n                    }\n                    return true;\n                case GLFW.GLFW_KEY_KP_ENTER:\n                case GLFW.GLFW_KEY_ENTER:\n                case GLFW.GLFW_KEY_RIGHT:\n                    if (visiblePossibilitiesIndex >= 0\n                            && visiblePossibilitiesIndex < visiblePossibilities.size()) {\n                        selectVisiblePossibility(visiblePossibilitiesIndex);\n                        return true;\n                    }\n            }\n        }\n        if (super.keyPressed(evt)) {\n            refreshDropdownList();\n            return true;\n        }\n        selectedDropdownPossibility = oldPossibility;\n        return false;\n    }\n\n    protected void selectVisiblePossibility(int index) {\n        visiblePossibilitiesIndex = index;\n        selectPossibility(visiblePossibilities.get(visiblePossibilitiesIndex));\n    }\n\n    public void selectPossibility(@Nullable IDropdownEntry<T> entry) {\n        selectedDropdownPossibility = entry;\n        setValue(selectedDropdownPossibility != null ? selectedDropdownPossibility.getDisplayString().getString() : \"\");\n        visiblePossibilities = Lists.newArrayList();\n        visiblePossibilitiesIndex = -1;\n        if (dropdownEntryListener != null) {\n            dropdownEntryListener.onSetDropdownPossiblity(selectedDropdownPossibility);\n        }\n    }\n\n    @Override\n    public void extractWidgetRenderState(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        // Display text red that is in an \"invalid\" state (no valid dropdrown entry selected)\n        this.setTextColor(this.selectedDropdownPossibility == null ? IModHelpers.get().getBaseHelpers().RGBAToInt(220, 10, 10, 255) : ARGB.opaque(14737632));\n\n        super.extractWidgetRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        if (this.isVisible() && isFocused()) {\n            Font fontRenderer = Minecraft.getInstance().font;\n            int yOffset = fontRenderer.lineHeight + 3;\n\n            int x = this.getX();\n            int y = this.getY() + yOffset;\n            int width = this.getWidth() + 9;\n            int startIndex = Math.max(0, Math.min(visiblePossibilitiesIndex, visiblePossibilities.size() - getDropdownSize()));\n            int endIndex = Math.min(startIndex + getDropdownSize(), visiblePossibilities.size());\n            int cy = y;\n\n            // Draw ... if we are not at the first element\n            if (startIndex > 0) {\n                // Draw background\n                guiGraphics.fill(x, cy - 1, x + width, cy + 11, -6250336);\n                guiGraphics.fill(x - 1, cy, x + width - 1, cy + 10, -16777216);\n\n                guiGraphics.text(fontRenderer, \"...\", x + 1, cy + 2, disabledColor, true);\n\n                cy += 10;\n            }\n\n            for (int i = startIndex; i < endIndex; i++) {\n                // Initialize entry\n                IDropdownEntry<?> dropdownEntry = visiblePossibilities.get(i);\n                MutableComponent possibility = dropdownEntry.getDisplayString();\n                List<FormattedCharSequence> displayPossibility = fontRenderer.split(possibility, width);\n                boolean active = visiblePossibilitiesIndex == i;\n                int entryHeight = yOffset;\n\n                // Optionally initialize tooltip\n                boolean addTooltip = (active && IModHelpers.get().getMinecraftClientHelpers().isShifted())\n                        || IModHelpers.get().getRenderHelpers().isPointInRegion(x, cy, getWidth(), yOffset, mouseX, mouseY);\n                List<MutableComponent> tooltipLines = null;\n                if (addTooltip) {\n                    tooltipLines = dropdownEntry.getTooltip();\n                    entryHeight += tooltipLines.size() * yOffset;\n                }\n\n                // Draw background\n                guiGraphics.fill(x, cy - 1, x + width, cy + entryHeight + 1, -6250336);\n                guiGraphics.fill(x - 1, cy, x + width - 1, cy + entryHeight, -16777216);\n\n                // Draw text\n                guiGraphics.text(fontRenderer, displayPossibility.get(0), x + 1, cy + 2, active ? enabledColor : disabledColor, true);\n                if(addTooltip) {\n                    int tooltipLineOffsetY = 2;\n                    for (Component tooltipLine : tooltipLines) {\n                        tooltipLineOffsetY += yOffset;\n                        guiGraphics.text(fontRenderer, tooltipLine.getString(), x + 1, cy + tooltipLineOffsetY, enabledColor, true);\n                    }\n                }\n\n                cy += entryHeight;\n            }\n\n            // Draw ... if we haven't reached the end of the list\n            if (endIndex < visiblePossibilities.size()) {\n                // Draw background\n                guiGraphics.fill(x, cy - 1, x + width, cy + 11, -6250336);\n                guiGraphics.fill(x - 1, cy, x + width - 1, cy + 10, -16777216);\n\n                guiGraphics.text(fontRenderer, \"...\", x + 1, cy + 2, disabledColor, true);\n            }\n        }\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        if (this.isVisible() && isFocused()) {\n            int i = getHoveredVisiblePossibility(evt.x(), evt.y());\n            if (i >= 0) {\n                selectVisiblePossibility(i);\n                return true;\n            }\n        }\n        return super.mouseClicked(evt, isDoubleClick);\n    }\n\n    public int getHoveredVisiblePossibility(double mouseX, double mouseY) {\n        Font fontRenderer = Minecraft.getInstance().gui.getFont();\n        int yOffset = fontRenderer.lineHeight + 3;\n\n        int x = this.getX();\n        int y = this.getY() + yOffset;\n        int startIndex = Math.max(0, Math.min(visiblePossibilitiesIndex, visiblePossibilities.size() - getDropdownSize()));\n        int endIndex = Math.min(startIndex + getDropdownSize(), visiblePossibilities.size());\n        int cy = y;\n\n        // Draw ... if we are not at the first element\n        if (startIndex > 0) {\n            cy += 10;\n        }\n\n        for (int i = startIndex; i < endIndex; i++) {\n            // Initialize entry\n            IDropdownEntry<?> dropdownEntry = visiblePossibilities.get(i);\n            boolean active = visiblePossibilitiesIndex == i;\n            int entryHeight = yOffset;\n\n            // Optionally initialize tooltip\n            boolean addTooltip = (active && IModHelpers.get().getMinecraftClientHelpers().isShifted())\n                    || IModHelpers.get().getRenderHelpers().isPointInRegion(x, cy, getWidth(), yOffset, mouseX, mouseY);\n            if (IModHelpers.get().getRenderHelpers().isPointInRegion(x, cy, getWidth(), yOffset, mouseX, mouseY)) {\n                return i;\n            }\n            List<MutableComponent> tooltipLines = null;\n            if (addTooltip) {\n                tooltipLines = dropdownEntry.getTooltip();\n                entryHeight += tooltipLines.size() * yOffset;\n            }\n\n            cy += entryHeight;\n        }\n\n        return -1;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/container/ContainerScreenAspectSettings.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.resources.language.I18n;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElement;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueTypeClient;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.client.gui.subgui.SubGuiHolder;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.SubGuiValueTypeInfoBase;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerAspectSettings;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.lwjgl.glfw.GLFW;\n\nimport java.util.List;\n\n/**\n * Gui for aspect settings.\n * @author rubensworks\n */\npublic class ContainerScreenAspectSettings extends ContainerScreenExtended<ContainerAspectSettings> {\n\n    private static final int ERROR_WIDTH = 13;\n    private static final int ERROR_HEIGHT = 13;\n    private static final int OK_WIDTH = 14;\n    private static final int OK_HEIGHT = 12;\n\n    private final List<IAspectPropertyTypeInstance> propertyTypes;\n    protected final SubGuiHolder subGuiHolder = new SubGuiHolder();\n    protected IGuiInputElementValueType<RenderPattern, ContainerScreenAspectSettings, ContainerAspectSettings, IGuiInputElementValueTypeClient<RenderPattern, ContainerScreenAspectSettings, ContainerAspectSettings>> guiElement = null;\n    protected int activePropertyIndex = 0;\n    protected RenderPattern propertyConfigPattern = null;\n    protected SubGuiValueTypeInfo propertyInfo = null;\n    private ButtonText buttonLeft = null;\n    private ButtonText buttonRight = null;\n    private ButtonText buttonExit = null;\n    private Component lastError;\n\n    public ContainerScreenAspectSettings(ContainerAspectSettings container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n\n        //noinspection deprecation\n        this.propertyTypes = Lists.newArrayList(container.getAspect().getDefaultProperties().getTypes());\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/aspect_settings.png\");\n    }\n\n    public int getActivePropertyIndex() {\n        return activePropertyIndex;\n    }\n\n    protected void saveSetting() {\n        if(guiElement != null && lastError == null) {\n            container.setValue(ValueDeseralizationContext.ofClient(), getActiveProperty(), guiElement.getValue());\n        }\n    }\n\n    protected void refreshButtonEnabled() {\n        buttonLeft.active = getActivePropertyIndex() > 0;\n        buttonRight.active = getActivePropertyIndex() < propertyTypes.size() - 1;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 213;\n    }\n\n    @Override\n    public void init() {\n        super.init();\n        subGuiHolder.init(this.leftPos, this.topPos);\n        addRenderableWidget(buttonExit = new ButtonText(leftPos + 7, topPos + 5, 12, 10, Component.translatable(\"gui.cyclopscore.up\"), Component.literal(\"<<\"), createServerPressable(ContainerAspectSettings.BUTTON_EXIT, (button) -> {\n            saveSetting();\n        }), true));\n        addRenderableWidget(buttonLeft = new ButtonText(leftPos + 21, topPos + 5, 10, 10, Component.translatable(\"gui.cyclopscore.left\"), Component.literal(\"<\"), (button) -> {\n            saveSetting();\n            if(getActivePropertyIndex() > 0) {\n                setActiveProperty(getActivePropertyIndex() - 1);\n                refreshButtonEnabled();\n            }\n        }, true));\n        addRenderableWidget(buttonRight = new ButtonText(leftPos + 159, topPos + 5, 10, 10, Component.translatable(\"gui.cyclopscore.right\"), Component.literal(\">\"), (button) -> {\n            saveSetting();\n            if(getActivePropertyIndex() < propertyTypes.size()) {\n                setActiveProperty(getActivePropertyIndex() + 1);\n                refreshButtonEnabled();\n            }\n        }, true));\n        refreshButtonEnabled();\n\n        setActiveProperty(activePropertyIndex);\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        subGuiHolder.renderBg(guiGraphics, this.leftPos, this.topPos, getMinecraft().getTextureManager(), font, partialTicks, mouseX, mouseY);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, mouseX, mouseY);\n        subGuiHolder.drawGuiContainerForegroundLayer(guiGraphics, this.leftPos, this.topPos, getMinecraft().getTextureManager(), font, mouseX, mouseY);\n\n        IAspectPropertyTypeInstance activeProperty = getActiveProperty();\n        if(activeProperty != null) {\n            String label = IModHelpers.get().getL10NHelpers().localize(activeProperty.getTranslationKey());\n            IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, label, 88, 10, 0,\n                    1.0F, 140, IModHelpers.get().getBaseHelpers().RGBAToInt(10, 10, 10, 255), false, Font.DisplayMode.NORMAL);\n            if (IModHelpers.get().getRenderHelpers().isPointInRegion(this.leftPos + 40, this.topPos, 110, 20, mouseX, mouseY)) {\n                String unlocalizedInfo = activeProperty.getTranslationKey() + \".info\";\n                if (I18n.exists(unlocalizedInfo)) {\n                    drawTooltip(Lists.newArrayList(Component.translatable(unlocalizedInfo)\n                            .withStyle(ChatFormatting.GRAY)), guiGraphics, mouseX, mouseY + 20);\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if(!subGuiHolder.charTyped(evt)) {\n            if (evt.codepoint() == 1 || this.getMinecraft().options.keyInventory.getKey().getValue() == evt.codepoint()) {\n                saveSetting();\n                this.getMinecraft().player.closeContainer();\n            } else {\n                return super.charTyped(evt);\n            }\n        } else {\n            if(guiElement != null) {\n                onValueChanged();\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (evt.key() != GLFW.GLFW_KEY_ESCAPE) {\n            if (this.subGuiHolder.keyPressed(evt)) {\n                if(guiElement != null) {\n                    onValueChanged();\n                }\n                return true;\n            } else {\n                return false;\n            }\n        } else {\n            saveSetting();\n            return super.keyPressed(evt);\n        }\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return subGuiHolder.mouseClicked(evt, isDoubleClick)\n                || super.mouseClicked(evt, isDoubleClick);\n    }\n\n    protected void onValueChanged() {\n        lastError = guiElement.validate();\n    }\n\n    protected IAspectPropertyTypeInstance getActiveProperty() {\n        return propertyTypes.get(Math.max(0, Math.min(propertyTypes.size() - 1, activePropertyIndex)));\n    }\n\n    protected void setActiveProperty(int index) {\n        onActivateElement(propertyTypes.get(activePropertyIndex = index));\n    }\n\n    protected void onActivateElement(IAspectPropertyTypeInstance property) {\n        // Deactivate old element\n        if(guiElement != null) {\n            guiElement.deactivate();\n            subGuiHolder.removeSubGui(propertyConfigPattern);\n            subGuiHolder.removeSubGui(propertyInfo);\n        }\n\n        // Determine element type\n        IValueTypeLogicProgrammerElement lpElement = property.getType().createLogicProgrammerElement();\n        guiElement = lpElement.createInnerGuiElement();\n        if (guiElement == null) {\n            throw new UnsupportedOperationException(\"Tried to invoke createInnerGuiElement on a value type that does not have an inner gui element: \" + property.getType().getTypeName());\n        }\n\n        // Create new element\n        guiElement.setValidator(property.getValidator());\n        subGuiHolder.addSubGui(propertyConfigPattern = guiElement.getClient().createSubGui(8, 17, 160, 91, this, (ContainerAspectSettings) getMenu()));\n        subGuiHolder.addSubGui(propertyInfo = new SubGuiValueTypeInfo(guiElement));\n        propertyConfigPattern.init(leftPos, topPos);\n        guiElement.activate();\n        syncInputValue();\n        lastError = guiElement.validate();\n    }\n\n    protected void syncInputValue() {\n        IAspectPropertyTypeInstance property = getActiveProperty();\n        IValue value = container.getPropertyValue(ValueDeseralizationContext.of(Minecraft.getInstance().player.level()), property);\n        if(value != null) {\n            guiElement.setValue(value);\n            guiElement.getClient().setValueInGui(propertyConfigPattern, false);\n        }\n        onValueChanged();\n    }\n\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        super.onUpdate(valueId, value);\n        IAspectPropertyTypeInstance property = container.getPropertyIds().get(valueId);\n        if(property != null && getActiveProperty() == property) {\n            syncInputValue();\n        }\n    }\n\n    public class SubGuiValueTypeInfo extends SubGuiValueTypeInfoBase<RenderPattern, ContainerScreenAspectSettings, ContainerAspectSettings> {\n\n        public SubGuiValueTypeInfo(IGuiInputElement<RenderPattern, ContainerScreenAspectSettings, ContainerAspectSettings, IGuiInputElementValueTypeClient<RenderPattern, ContainerScreenAspectSettings, ContainerAspectSettings>> element) {\n            super(ContainerScreenAspectSettings.this, (ContainerAspectSettings) ContainerScreenAspectSettings.this.container, element, 8, 105, 160, 20);\n        }\n\n        @Override\n        protected boolean showError() {\n            return true;\n        }\n\n        @Override\n        protected Component getLastError() {\n            return lastError;\n        }\n\n        @Override\n        protected Identifier getTexture() {\n            return texture;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/container/ContainerScreenMultipart.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonImage;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.client.gui.image.IImage;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipart;\nimport org.cyclops.integrateddynamics.core.part.PartTypeConfigurable;\n\nimport java.awt.*;\n\n\n/**\n * Gui for parts.\n * @author rubensworks\n */\npublic abstract class ContainerScreenMultipart<P extends IPartType<P, S>, S extends IPartState<P>, C extends ContainerMultipart<P, S>>\n        extends ContainerScreenExtended<C> {\n\n    private static final Rectangle ITEM_POSITION = new Rectangle(8, 17, 18, 18);\n\n    protected final DisplayErrorsComponent displayErrors = new DisplayErrorsComponent();\n\n    public ContainerScreenMultipart(C container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    public void init() {\n        clearWidgets();\n        super.init();\n        P partType = getMenu().getPartType();\n        if(partType instanceof PartTypeConfigurable && partType.getContainerProviderSettings(null).isPresent()) {\n            addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos + 0, 18, 18,\n                    Component.translatable(\"gui.integrateddynamics.partsettings\"),\n                    createServerPressable(ContainerMultipart.BUTTON_SETTINGS, (button) -> {}),\n                    new IImage[]{\n                            org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_BACKGROUND_INACTIVE,\n                            org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_MIDDLE_SETTINGS\n                    },\n                    false, 0, 0));\n            if (getMenu().getPartType().supportsOffsets()) {\n                addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos + 20, 18, 18,\n                        Component.translatable(\"gui.integrateddynamics.part_offsets\"),\n                        createServerPressable(ContainerMultipart.BUTTON_OFFSETS, (button) -> {\n                        }),\n                        new IImage[]{\n                                org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_BACKGROUND_INACTIVE,\n                                org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_MIDDLE_OFFSET\n                        },\n                        false, 0, 0));\n            }\n        }\n    }\n\n    protected abstract String getNameId();\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/\" + getNameId() + \".png\");\n    }\n\n    protected float colorSmoothener(float color) {\n        return 1F - ((1F - color) / 4F);\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n\n        // Draw part name\n        guiGraphics.text(font, getTitle(), leftPos + 8, topPos + 6, ARGB.opaque(4210752), false);\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, x, y);\n\n        if (isHovering(-20, 0, 18, 18, mouseX, mouseY)) {\n            drawTooltip(Lists.newArrayList(Component.translatable(\"gui.integrateddynamics.part_settings\")), guiGraphics, mouseX, mouseY);\n        }\n        if (isHovering(-20, 20, 18, 18, mouseX, mouseY)) {\n            drawTooltip(Lists.newArrayList(Component.translatable(\"gui.integrateddynamics.part_offsets\")), guiGraphics, mouseX, mouseY);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/container/ContainerScreenMultipartAspects.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonImage;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenScrolling;\nimport org.cyclops.cyclopscore.client.gui.image.IImage;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipartAspects;\n\nimport java.awt.*;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Gui for parts.\n * @author rubensworks\n */\npublic abstract class ContainerScreenMultipartAspects<P extends IPartType<P, S>, S extends IPartState<P>, A extends IAspect<?, ?>, C extends ContainerMultipartAspects<P, S, A>>\n        extends ContainerScreenScrolling<C> {\n\n    private static final Rectangle ITEM_POSITION = new Rectangle(8, 17, 18, 18);\n\n    protected final DisplayErrorsComponent displayErrors = new DisplayErrorsComponent();\n\n    private Map<IAspect, ButtonText> aspectPropertyButtons = Maps.newHashMap();\n\n    public ContainerScreenMultipartAspects(C container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    @Override\n    protected Rectangle getScrollRegion() {\n        return new Rectangle(this.leftPos + 9, this.topPos + 18, 160, 105);\n    }\n\n    @Override\n    public void init() {\n        clearWidgets();\n        super.init();\n        if(getMenu().getPartType().getContainerProviderSettings(null).isPresent()) {\n            addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos + 0, 18, 18,\n                    Component.translatable(\"gui.integrateddynamics.partsettings\"),\n                    createServerPressable(ContainerMultipartAspects.BUTTON_SETTINGS, (button) -> {}),\n                    new IImage[]{\n                            org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_BACKGROUND_INACTIVE,\n                            org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_MIDDLE_SETTINGS\n                    },\n                    false, 0, 0));\n            if (getMenu().getPartType().supportsOffsets()) {\n                addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos + 20, 18, 18,\n                        Component.translatable(\"gui.integrateddynamics.part_offsets\"),\n                        createServerPressable(ContainerMultipartAspects.BUTTON_OFFSETS, (button) -> {\n                        }),\n                        new IImage[]{\n                                org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_BACKGROUND_INACTIVE,\n                                org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_MIDDLE_OFFSET\n                        },\n                        false, 0, 0));\n            }\n        }\n        for(Map.Entry<IAspect, String> entry : getMenu().getAspectPropertyButtons().entrySet()) {\n            ButtonText button = new ButtonText(-20, -20, 10, 10,\n                    Component.translatable(\"gui.integrateddynamics.aspect_settings\"), Component.literal(\"+\"),\n                    createServerPressable(entry.getValue(), b -> {}), true);\n            aspectPropertyButtons.put(entry.getKey(), button);\n            addRenderableWidget(button);\n        }\n    }\n\n    protected abstract String getNameId();\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/\" + getNameId() + \".png\");\n    }\n\n    protected float colorSmoothener(float color) {\n        return 1F - ((1F - color) / 4F);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n\n        // Reset button positions\n        for(Map.Entry<IAspect, ButtonText> entry : this.aspectPropertyButtons.entrySet()) {\n            entry.getValue().setX(-20);\n            entry.getValue().setY(-20);\n        }\n\n        // Draw part name\n        IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, title.getString(),\n                this.leftPos + offsetX + 6, this.topPos + offsetY + 10, 70, ARGB.opaque(4210752), false, Font.DisplayMode.NORMAL);\n\n        // Draw aspects\n        C container = getMenu();\n        int aspectBoxHeight = container.getAspectBoxHeight();\n        for(int i = 0; i < container.getPageSize(); i++) {\n            if(container.isElementVisible(i)) {\n                A aspect = container.getVisibleElement(i);\n\n                //GlStateManager._disableAlphaTest();\n                Triple<Float, Float, Float> rgb = IModHelpers.get().getBaseHelpers().intToRGB(aspect.getValueType().getDisplayColor());\n                int color = ARGB.colorFromFloat(\n                        1F,\n                        colorSmoothener(rgb.getLeft()),\n                        colorSmoothener(rgb.getMiddle()),\n                        colorSmoothener(rgb.getRight())\n                );\n\n                // Background\n                guiGraphics.blit(RenderPipelines.GUI_TEXTURED, texture, leftPos + offsetX + 9,\n                        topPos + offsetY + 18 + aspectBoxHeight * i, 0, getBaseYSize(), 160, aspectBoxHeight - 1, 256, 256, color);\n\n                // Aspect type info\n                String aspectName = IModHelpers.get().getL10NHelpers().localize(aspect.getTranslationKey());\n                IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, font, aspectName,\n                        this.leftPos + offsetX + 26,\n                        this.topPos + offsetY + 25 + aspectBoxHeight * i,\n                        getMaxLabelWidth(), IModHelpers.get().getBaseHelpers().RGBAToInt(40, 40, 40, 255), false, Font.DisplayMode.NORMAL);\n\n                drawAdditionalElementInfo(guiGraphics, container, i, aspect);\n\n                if(aspectPropertyButtons.containsKey(aspect)) {\n                    ButtonText button = aspectPropertyButtons.get(aspect);\n                    button.setX(this.leftPos + offsetX + 116);\n                    button.setY(this.topPos + offsetY + 20 + aspectBoxHeight * i);\n                }\n            }\n        }\n    }\n\n    protected abstract void drawAdditionalElementInfo(GuiGraphicsExtractor guiGraphics, C container, int index, A aspect);\n\n    protected Rectangle getElementPosition(C container, int i, boolean absolute) {\n        return new Rectangle(ITEM_POSITION.x + offsetX + (absolute ? this.leftPos : 0),\n                             ITEM_POSITION.y + container.getAspectBoxHeight() * i + offsetY + (absolute ? this.topPos : 0),\n                             ITEM_POSITION.width, ITEM_POSITION.height\n        );\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, mouseX, mouseY);\n        C container = getMenu();\n        for(int i = 0; i < container.getPageSize(); i++) {\n            if(container.isElementVisible(i)) {\n                // Item icon tooltip\n                if(isPointInRegion(getElementPosition(container, i, false), new Point(mouseX, mouseY))) {\n                    List<Component> lines = Lists.newLinkedList();\n                    container.getVisibleElement(i).loadTooltip(lines::add, true);\n                    drawTooltip(lines, guiGraphics, mouseX, mouseY);\n                }\n                drawAdditionalElementInfoForeground(guiGraphics, container, i, container.getVisibleElement(i), mouseX, mouseY);\n\n                // Optional aspect properties tooltip\n                IAspect aspect = container.getVisibleElement(i);\n                if(aspectPropertyButtons.containsKey(aspect)) {\n                    ButtonText button = aspectPropertyButtons.get(aspect);\n                    int x = button.getX() - leftPos;\n                    int y = button.getY() - topPos;\n                    if(isHovering(x, y, button.getWidth(), button.getHeight(), mouseX, mouseY)) {\n                        List<Component> lines = Lists.newLinkedList();\n                        lines.add(Component.translatable(\"gui.integrateddynamics.part.properties\")\n                                .withStyle(ChatFormatting.WHITE));\n                        for(IAspectPropertyTypeInstance property : ((IAspect<?, ?>) aspect).getPropertyTypes()) {\n                            lines.add(Component.literal(\"-\")\n                                    .withStyle(ChatFormatting.YELLOW)\n                                    .append(Component.translatable(property.getTranslationKey())));\n                        }\n                        drawTooltip(lines, guiGraphics, mouseX, mouseY);\n                    }\n                }\n            }\n        }\n\n        if (isHovering(-20, 0, 18, 18, mouseX, mouseY)) {\n            drawTooltip(Lists.newArrayList(Component.translatable(\"gui.integrateddynamics.part_settings\")), guiGraphics, mouseX, mouseY);\n        }\n        if (isHovering(-20, 20, 18, 18, mouseX, mouseY)) {\n            drawTooltip(Lists.newArrayList(Component.translatable(\"gui.integrateddynamics.part_offsets\")), guiGraphics, mouseX, mouseY);\n        }\n    }\n\n    protected abstract void drawAdditionalElementInfoForeground(GuiGraphicsExtractor guiGraphics, C container, int index,\n                                                                A aspect, int mouseX, int mouseY);\n\n    public int getMaxLabelWidth() {\n        return 63;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/container/ContainerScreenPartOffset.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetNumberField;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.client.gui.image.IImage;\nimport org.cyclops.cyclopscore.client.gui.image.Images;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartOffset;\nimport org.lwjgl.glfw.GLFW;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Gui for part offsets.\n * @author rubensworks\n */\npublic class ContainerScreenPartOffset<T extends ContainerPartOffset> extends ContainerScreenExtended<T> {\n\n    private WidgetNumberField numberFieldX = null;\n    private WidgetNumberField numberFieldY = null;\n    private WidgetNumberField numberFieldZ = null;\n\n    public ContainerScreenPartOffset(T container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    protected void onSave() {\n        try {\n            ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastXValueId(), numberFieldX.getInt());\n            ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastYValueId(), numberFieldY.getInt());\n            ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastZValueId(), numberFieldZ.getInt());\n        } catch (NumberFormatException e) { }\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/part_offsets.png\");\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        numberFieldX = new WidgetNumberField(font, leftPos + 107 - 54 - 7 - 18, topPos + 33, 46, 14, true,\n                Component.translatable(\"gui.integrateddynamics.partsettings.partoffset.x\"), true);\n        numberFieldX.setMaxLength(4);\n        numberFieldX.setMaxValue(GeneralConfig.maxPartOffset);\n        numberFieldX.setMinValue(-GeneralConfig.maxPartOffset);\n        numberFieldX.setVisible(true);\n        numberFieldX.setTextColor(ARGB.opaque(16777215));\n        numberFieldX.setCanLoseFocus(true);\n\n        numberFieldY = new WidgetNumberField(font, leftPos + 107 - 54 + 36 - 7, topPos + 33, 46, 14, true,\n                Component.translatable(\"gui.integrateddynamics.partsettings.partoffset.x\"), true);\n        numberFieldY.setMaxLength(4);\n        numberFieldY.setMaxValue(GeneralConfig.maxPartOffset);\n        numberFieldY.setMinValue(-GeneralConfig.maxPartOffset);\n        numberFieldY.setVisible(true);\n        numberFieldY.setTextColor(ARGB.opaque(16777215));\n        numberFieldY.setCanLoseFocus(true);\n\n        numberFieldZ = new WidgetNumberField(font, leftPos + 107 - 54 + 72 - 7 + 18, topPos + 33, 46, 14, true,\n                Component.translatable(\"gui.integrateddynamics.partsettings.partoffset.x\"), true);\n        numberFieldZ.setMaxLength(4);\n        numberFieldZ.setMaxValue(GeneralConfig.maxPartOffset);\n        numberFieldZ.setMinValue(-GeneralConfig.maxPartOffset);\n        numberFieldZ.setVisible(true);\n        numberFieldZ.setTextColor(ARGB.opaque(16777215));\n        numberFieldZ.setCanLoseFocus(true);\n\n        MutableComponent save = Component.translatable(\"gui.integrateddynamics.button.save\");\n        addRenderableWidget(new ButtonText(this.leftPos + 178, this.topPos + 6, font.width(save.getVisualOrderText()) + 6, 16, save, save,\n                createServerPressable(ContainerPartOffset.BUTTON_SAVE, b -> onSave()), true));\n\n        this.refreshValues();\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (!this.numberFieldX.charTyped(evt)\n                && !this.numberFieldY.charTyped(evt)\n                && !this.numberFieldZ.charTyped(evt)) {\n            onSave();\n            return super.charTyped(evt);\n        }\n        return true;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (evt.key() != GLFW.GLFW_KEY_ESCAPE) {\n            if (this.numberFieldX.keyPressed(evt)\n                    || this.numberFieldY.keyPressed(evt)\n                    || this.numberFieldZ.keyPressed(evt)) {\n                onSave();\n                return true;\n            }\n            return true;\n        } else {\n            return super.keyPressed(evt);\n        }\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        if (this.numberFieldX.mouseClicked(evt, isDoubleClick)\n                || this.numberFieldY.mouseClicked(evt, isDoubleClick)\n                || this.numberFieldZ.mouseClicked(evt, isDoubleClick)) {\n            onSave();\n            return true;\n        }\n        return super.mouseClicked(evt, isDoubleClick);\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n\n        guiGraphics.text(font, Component.translatable(\"gui.integrateddynamics.part_offsets\"), this.titleLabelX, this.titleLabelY, ARGB.opaque(4210752), false);\n\n        guiGraphics.text(font, \"X\", leftPos + 45 + 5, topPos + 19, ARGB.opaque(0), false);\n        guiGraphics.text(font, \"Y\", leftPos + 99 + 5, topPos + 19, ARGB.opaque(0), false);\n        guiGraphics.text(font, \"Z\", leftPos + 153 + 5, topPos + 19, ARGB.opaque(0), false);\n\n        numberFieldX.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        numberFieldY.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        numberFieldZ.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n\n        for (int i = 0; i < 3; i++) {\n            int x = leftPos + 64 + i * 54;\n            if (getMenu().isOffsetVariableFilled(i)) {\n                IImage image = container.getOffsetVariableError(i) == null ? Images.OK : Images.ERROR;\n                image.draw(guiGraphics, x, topPos + 52);\n            }\n        }\n\n        if (getMenu().getMaxOffset() == 0) {\n            Images.ERROR.draw(guiGraphics, leftPos + 74, topPos + 3);\n        }\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        if (isHovering(0, 0, 90, 18, mouseX, mouseY)) {\n            List<Component> lines = Lists.newArrayList(\n                    Component.translatable(\"gui.integrateddynamics.partoffset.offsets\"),\n                    Component.translatable(\"gui.integrateddynamics.partoffset.offsets.max\", getMenu().getMaxOffset())\n                            .withStyle(ChatFormatting.GRAY)\n            );\n            if (getMenu().getMaxOffset() == 0) {\n                lines.add(Component.translatable(\"gui.integrateddynamics.partoffset.offsets.max.howtoincrease\", getMenu().getMaxOffset())\n                        .withStyle(ChatFormatting.RED));\n            }\n            drawTooltip(lines, guiGraphics, mouseX, mouseY);\n        }\n\n        for (int i = 0; i < 3; i++) {\n            int x = 64 + i * 54;\n            int slot = i;\n            IModHelpers.get().getGuiHelpers().renderTooltipOptional(this, guiGraphics, x, 52, 14, 13, mouseX, mouseY,\n                    () -> {\n                        Component unlocalizedMessage = container.getOffsetVariableError(slot);\n                        if (unlocalizedMessage != null) {\n                            return Optional.of(Collections.singletonList(unlocalizedMessage));\n                        }\n                        return Optional.empty();\n                    });\n        }\n    }\n\n    @Override\n    protected int getBaseXSize() {\n        return 214;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 155;\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        if (valueId == getMenu().getLastXValueId()) {\n            numberFieldX.setValue(Integer.toString(getMenu().getLastXValue()));\n        }\n        if (valueId == getMenu().getLastYValueId()) {\n            numberFieldY.setValue(Integer.toString(getMenu().getLastYValue()));\n        }\n        if (valueId == getMenu().getLastZValueId()) {\n            numberFieldZ.setValue(Integer.toString(getMenu().getLastZValue()));\n        }\n\n        numberFieldX.setEditable(!getMenu().isOffsetVariableFilled(0));\n        numberFieldY.setEditable(!getMenu().isOffsetVariableFilled(1));\n        numberFieldZ.setEditable(!getMenu().isOffsetVariableFilled(2));\n\n        if (valueId == getMenu().getMaxOffsetId()) {\n            int max = getMenu().getMaxOffset();\n            numberFieldX.setMaxValue(max);\n            numberFieldX.setMinValue(-max);\n            numberFieldY.setMaxValue(max);\n            numberFieldY.setMinValue(-max);\n            numberFieldZ.setMaxValue(max);\n            numberFieldZ.setMinValue(-max);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/container/ContainerScreenPartSettings.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetNumberField;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntry;\nimport org.cyclops.integrateddynamics.core.client.gui.WidgetTextFieldDropdown;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartSettings;\nimport org.lwjgl.glfw.GLFW;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.stream.Collectors;\n\n/**\n * Gui for part settings.\n * @author rubensworks\n */\npublic class ContainerScreenPartSettings<T extends ContainerPartSettings> extends ContainerScreenExtended<T> {\n\n    private WidgetNumberField numberFieldUpdateInterval = null;\n    private WidgetNumberField numberFieldPriority = null;\n    private WidgetNumberField numberFieldChannel = null;\n    private WidgetTextFieldDropdown<Direction> dropdownFieldSide = null;\n    private List<SideDropdownEntry> dropdownEntries;\n\n    public ContainerScreenPartSettings(T container, Inventory inventory, Component title) {\n        super(container, inventory, title);\n    }\n\n    protected void onSave() {\n        try {\n            if (isFieldSideEnabled()) {\n                Direction selectedSide = dropdownFieldSide.getSelectedDropdownPossibility() == null ? null : dropdownFieldSide.getSelectedDropdownPossibility().getValue();\n                int side = selectedSide != null && selectedSide != getDefaultSide() ? selectedSide.ordinal() : -1;\n                ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastSideValueId(), side);\n            }\n            if (isFieldUpdateIntervalEnabled()) {\n                int updateInterval = numberFieldUpdateInterval.getInt();\n                ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastUpdateValueId(), updateInterval);\n            }\n            if (isFieldPriorityEnabled()) {\n                int priority = numberFieldPriority.getInt();\n                ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastPriorityValueId(), priority);\n            }\n            if (isFieldChannelEnabled()) {\n                int channel = numberFieldChannel.getInt();\n                ValueNotifierHelpers.setValue(getMenu(), getMenu().getLastChannelValueId(), channel);\n            }\n        } catch (NumberFormatException e) { }\n    }\n\n    @Override\n    protected Identifier constructGuiTexture() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/part_settings.png\");\n    }\n\n    protected Direction getCurrentSide() {\n        return getMenu().getTarget().getTarget().getSide();\n    }\n\n    protected Direction getDefaultSide() {\n        return getMenu().getTarget().getCenter().getSide().getOpposite();\n    }\n\n    protected String getSideText(Direction side) {\n        return side.getSerializedName().toLowerCase(Locale.ENGLISH);\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        if (isFieldSideEnabled()) {\n            dropdownEntries = Arrays.stream(Direction.values()).map(SideDropdownEntry::new).collect(Collectors.toList());\n            dropdownFieldSide = new WidgetTextFieldDropdown(font, leftPos + 106, topPos + getFieldSideY(),\n                    70, 14, Component.translatable(\"gui.integrateddynamics.partsettings.side\"), true,\n                    Sets.newHashSet(dropdownEntries));\n            setSideInDropdownField(getCurrentSide());\n            dropdownFieldSide.setMaxLength(15);\n            dropdownFieldSide.setVisible(true);\n            dropdownFieldSide.setTextColor(ARGB.opaque(16777215));\n            dropdownFieldSide.setCanLoseFocus(true);\n        }\n\n        if (isFieldUpdateIntervalEnabled()) {\n            numberFieldUpdateInterval = new WidgetNumberField(font, leftPos + 106, topPos + getFieldUpdateIntervalY(), 70, 14, true,\n                    Component.translatable(\"gui.integrateddynamics.partsettings.update_interval\"), true);\n            numberFieldUpdateInterval.setMaxLength(15);\n            numberFieldUpdateInterval.setVisible(true);\n            numberFieldUpdateInterval.setTextColor(ARGB.opaque(16777215));\n            numberFieldUpdateInterval.setCanLoseFocus(true);\n            numberFieldUpdateInterval.setMinValue(container.getLastMinUpdateValue());\n        }\n\n        if (isFieldPriorityEnabled()) {\n            numberFieldPriority = new WidgetNumberField(font, leftPos + 106, topPos + getFieldPriorityY(), 70, 14, true,\n                    Component.translatable(\"gui.integrateddynamics.partsettings.priority\"), true);\n            numberFieldPriority.setPositiveOnly(false);\n            numberFieldPriority.setMaxLength(15);\n            numberFieldPriority.setVisible(true);\n            numberFieldPriority.setTextColor(ARGB.opaque(16777215));\n            numberFieldPriority.setCanLoseFocus(true);\n        }\n\n        if (isFieldChannelEnabled()) {\n            numberFieldChannel = new WidgetNumberField(font, leftPos + 106, topPos + getFieldChannelY(), 70, 14, true,\n                    Component.translatable(\"gui.integrateddynamics.partsettings.channel\"), true);\n            numberFieldChannel.setPositiveOnly(false);\n            numberFieldChannel.setMaxLength(15);\n            numberFieldChannel.setVisible(true);\n            numberFieldChannel.setTextColor(ARGB.opaque(16777215));\n            numberFieldChannel.setCanLoseFocus(true);\n            numberFieldChannel.setEditable(isChannelEnabled());\n        }\n\n        MutableComponent save = Component.translatable(\"gui.integrateddynamics.button.save\");\n        addRenderableWidget(new ButtonText(this.leftPos + 178, this.topPos + 8, font.width(save.getVisualOrderText()) + 6, 16, save, save,\n                createServerPressable(ContainerPartSettings.BUTTON_SAVE, b -> onSave()), true));\n\n        this.refreshValues();\n    }\n\n    protected int getFieldSideY() {\n        return 9;\n    }\n\n    protected int getFieldUpdateIntervalY() {\n        return 34;\n    }\n\n    protected int getFieldPriorityY() {\n        return 59;\n    }\n\n    protected int getFieldChannelY() {\n        return 84;\n    }\n\n    protected boolean isFieldSideEnabled() {\n        return true;\n    }\n\n    protected boolean isFieldUpdateIntervalEnabled() {\n        return true;\n    }\n\n    protected boolean isFieldPriorityEnabled() {\n        return true;\n    }\n\n    protected boolean isFieldChannelEnabled() {\n        return true;\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (!(isFieldUpdateIntervalEnabled() && this.numberFieldUpdateInterval.charTyped(evt))\n                && !(isFieldPriorityEnabled() && this.numberFieldPriority.charTyped(evt))\n                && !(isFieldChannelEnabled() && this.numberFieldChannel.charTyped(evt))\n                && !(isFieldSideEnabled() && this.dropdownFieldSide.charTyped(evt))) {\n            return super.charTyped(evt);\n        }\n        return true;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (evt.key() != GLFW.GLFW_KEY_ESCAPE) {\n            if (isFieldSideEnabled()) {\n                if (this.dropdownFieldSide.keyPressed(evt)) {\n                    return true;\n                }\n            }\n            if (isFieldUpdateIntervalEnabled()) {\n                if (this.numberFieldUpdateInterval.keyPressed(evt)) {\n                    return true;\n                }\n            }\n            if (isFieldPriorityEnabled()) {\n                if (this.numberFieldPriority.keyPressed(evt)) {\n                    return true;\n                }\n            }\n            if (isFieldChannelEnabled()) {\n                if (this.numberFieldChannel.keyPressed(evt)) {\n                    return true;\n                }\n            }\n            return true;\n        } else {\n            return super.keyPressed(evt);\n        }\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        if (isFieldSideEnabled()) {\n            if (this.dropdownFieldSide.mouseClicked(evt, isDoubleClick)) {\n                return true;\n            }\n        }\n        if (isFieldUpdateIntervalEnabled()) {\n            if (this.numberFieldUpdateInterval.mouseClicked(evt, isDoubleClick)) {\n                return true;\n            }\n        }\n        if (isFieldPriorityEnabled()) {\n            if (this.numberFieldPriority.mouseClicked(evt, isDoubleClick)) {\n                return true;\n            }\n        }\n        if (isFieldChannelEnabled()) {\n            if (this.numberFieldChannel.mouseClicked(evt, isDoubleClick)) {\n                return true;\n            }\n        }\n        return super.mouseClicked(evt, isDoubleClick);\n    }\n\n    @Override\n    public void extractBackground(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        super.extractBackground(guiGraphics, mouseX, mouseY, partialTicks);\n        if (isFieldUpdateIntervalEnabled()) {\n            guiGraphics.text(font, IModHelpers.get().getL10NHelpers().localize(\"gui.integrateddynamics.partsettings.update_interval\"), leftPos + 8, topPos + getFieldUpdateIntervalY() + 3, ARGB.opaque(0), false);\n            numberFieldUpdateInterval.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n        if (isFieldPriorityEnabled()) {\n            guiGraphics.text(font, IModHelpers.get().getL10NHelpers().localize(\"gui.integrateddynamics.partsettings.priority\"), leftPos + 8, topPos + getFieldPriorityY() + 3, ARGB.opaque(0), false);\n            numberFieldPriority.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n        if (isFieldChannelEnabled()) {\n            guiGraphics.text(font, IModHelpers.get().getL10NHelpers().localize(\"gui.integrateddynamics.partsettings.channel\"), leftPos + 8, topPos + getFieldChannelY() + 3, isChannelEnabled() ? ARGB.opaque(0) : IModHelpers.get().getBaseHelpers().RGBAToInt(100, 100, 100, 255), false);\n            numberFieldChannel.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n        if (isFieldSideEnabled()) {\n            guiGraphics.text(font, IModHelpers.get().getL10NHelpers().localize(\"gui.integrateddynamics.partsettings.side\"), leftPos + 8, topPos + getFieldSideY() + 3, ARGB.opaque(0), false);\n            dropdownFieldSide.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n    }\n\n    @Override\n    protected void extractLabels(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY) {\n        // super.drawGuiContainerForegroundLayer(matrixStack, mouseX, mouseY);\n        if (!isChannelEnabled()) {\n            IModHelpers.get().getGuiHelpers().renderTooltip(this, guiGraphics, 8, getFieldChannelY() + 3, 100, 20, mouseX, mouseY,\n                    () -> Lists.<Component>newArrayList(Component.translatable(\"gui.integrateddynamics.partsettings.channel.disabledinfo\")));\n        }\n    }\n\n    protected boolean isChannelEnabled() {\n        return GeneralConfig.energyConsumptionMultiplier > 0;\n    }\n\n    @Override\n    protected int getBaseXSize() {\n        return 214;\n    }\n\n    @Override\n    protected int getBaseYSize() {\n        return 191;\n    }\n\n    protected void setSideInDropdownField(Direction side) {\n        dropdownFieldSide.selectPossibility(dropdownEntries.get(side.ordinal()));\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        if (isFieldSideEnabled() && valueId == getMenu().getLastSideValueId()) {\n            int side = getMenu().getLastSideValue();\n            setSideInDropdownField(side == -1 ? getDefaultSide() : Direction.values()[side]);\n        }\n        if (isFieldUpdateIntervalEnabled() && valueId == getMenu().getLastUpdateValueId()) {\n            numberFieldUpdateInterval.setValue(Integer.toString(getMenu().getLastUpdateValue()));\n        }\n        if (isFieldUpdateIntervalEnabled() && valueId == getMenu().getLastMinUpdateValueId()) {\n            numberFieldUpdateInterval.setMinValue(getMenu().getLastMinUpdateValue());\n        }\n        if (isFieldPriorityEnabled() && valueId == getMenu().getLastPriorityValueId()) {\n            numberFieldPriority.setValue(Integer.toString(getMenu().getLastPriorityValue()));\n        }\n        if (isFieldChannelEnabled() && valueId == getMenu().getLastChannelValueId()) {\n            numberFieldChannel.setValue(Integer.toString(getMenu().getLastChannelValue()));\n        }\n    }\n\n    public class SideDropdownEntry implements IDropdownEntry<Direction> {\n\n        private final Direction side;\n\n        public SideDropdownEntry(Direction side) {\n            this.side = side;\n        }\n\n        @Override\n        public String getMatchString() {\n            return getSideText(side);\n        }\n\n        @Override\n        public MutableComponent getDisplayString() {\n            if (getDefaultSide() == this.side) {\n                return Component.literal(getMatchString()).withStyle(ChatFormatting.YELLOW);\n            }\n            return Component.literal(getMatchString());\n        }\n\n        @Override\n        public List<MutableComponent> getTooltip() {\n            return Collections.emptyList();\n        }\n\n        @Override\n        public Direction getValue() {\n            return this.side;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/container/DisplayErrorsComponent.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.client.gui.image.Images;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.StringHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * A component for displaying errors.\n * @author rubensworks\n */\npublic class DisplayErrorsComponent {\n\n    public void drawForeground(GuiGraphicsExtractor guiGraphics, @Nullable List<Component> errors, int errorX, int errorY, int mouseX, int mouseY, ContainerScreenExtended<?> gui, int guiLeft, int guiTop) {\n        if(errors != null && !errors.isEmpty()) {\n            if(gui.isHovering(errorX, errorY, Images.ERROR.getSheetWidth(), Images.ERROR.getSheetHeight(), mouseX, mouseY)) {\n                List<Component> lines = Lists.newLinkedList();\n                for(Component error : errors) {\n                    lines.addAll(StringHelpers.splitLines(error.getString(), IModHelpers.get().getL10NHelpers().getMaxTooltipLineLength(),\n                            ChatFormatting.RED.toString())\n                            .stream()\n                            .map(Component::literal)\n                            .collect(Collectors.toList()));\n                }\n                gui.drawTooltip(lines, guiGraphics, mouseX, mouseY);\n            }\n        }\n    }\n\n    public void drawBackground(GuiGraphicsExtractor guiGraphics, @Nullable List<Component> errors, int errorX, int errorY, int okX, int okY, ContainerScreenExtended<?> gui, int guiLeft, int guiTop, boolean okCondition) {\n        // Render error symbol\n        if(errors != null && !errors.isEmpty()) {\n            Images.ERROR.draw(guiGraphics, guiLeft + errorX, guiTop + errorY);\n        } else if(okCondition) {\n            Images.OK.draw(guiGraphics, guiLeft + okX, guiTop + okY);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/subgui/SubGuiBox.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.subgui;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\n\nimport java.util.List;\n\n/**\n * A sub gui that simply renders a box.\n * @author rubensworks\n */\npublic abstract class SubGuiBox implements ISubGuiBox {\n\n    protected static final Identifier TEXTURE = Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/sub_gui.png\");\n\n    private final Box type;\n\n    protected List<Button> buttonList = Lists.newArrayList();\n    protected final SubGuiHolder subGuiHolder = new SubGuiHolder();\n\n    public SubGuiBox(Box type) {\n        this.type = type;\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        buttonList.clear();\n        subGuiHolder.init(guiLeft, guiTop);\n    }\n\n    public void drawScreen(GuiGraphicsExtractor guiGraphics, int mouseX, int mouseY, float partialTicks) {\n        for (int i = 0; i < this.buttonList.size(); ++i) {\n            this.buttonList.get(i).extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n    }\n\n    protected boolean isDrawBackground() {\n        return true;\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        if (this.isDrawBackground()) {\n            int textureWidth = 19;\n            int textureHeight = textureWidth;\n\n            int x = guiLeft + getX();\n            int y = guiTop + getY();\n            int width = getWidth();\n            int height = getHeight();\n            int tx = type.getX();\n            int ty = type.getY();\n\n            // Corners\n            guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x, y, tx, tx, 1, 1, 256, 256); // top left\n            guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x + width - 1, y, tx + textureWidth - 1, ty, 1, 1, 256, 256); // top right\n            guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x, y + height - 1, 0, tx + textureHeight - 1, ty + 1, 1, 256, 256); // bottom left\n            guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x + width - 1, y + height - 1, tx + textureWidth - 1, ty + textureHeight - 1, 1, 1, 256, 256); // bottom right\n\n            int i, j;\n\n            // Sides\n            i = 1;\n            while (i < width - 1) {\n                int currentWidth = Math.max(1, Math.min(width - i, textureWidth - 2) - 1);\n                guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x + i, y, tx + 1, ty, currentWidth, 1, 256, 256);\n                guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x + i, y + height - 1, tx + 1, ty + textureHeight - 1, currentWidth, 1, 256, 256);\n                i += currentWidth;\n            }\n\n            i = 1;\n            while (i < height - 1) {\n                int currentHeight = Math.max(1, Math.min(height - i, textureHeight - 2) - 1);\n                guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x, y + i, tx, ty + 1, 1, currentHeight, 256, 256);\n                guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x + width - 1, y + i, tx + textureWidth - 1, ty + 1, 1, currentHeight, 256, 256);\n                i += currentHeight;\n            }\n\n            // Center\n            i = 1;\n            while (i < width - 1) {\n                int currentWidth = Math.max(1, Math.min(width - i, textureWidth - 2) - 1);\n                j = 1;\n                while (j < height - 1) {\n                    int currentHeight = Math.max(1, Math.min(height - j, textureHeight - 2) - 1);\n                    guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x + i, y + j, tx + 1, ty + 1, currentWidth, currentHeight, 256, 256);\n                    j += currentHeight;\n                }\n                i += currentWidth;\n            }\n        }\n\n        // Draw buttons\n        drawScreen(guiGraphics, mouseX, mouseY, partialTicks);\n\n        subGuiHolder.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        subGuiHolder.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        return subGuiHolder.charTyped(evt);\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        return subGuiHolder.keyPressed(evt);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        subGuiHolder.mouseClicked(evt, isDoubleClick);\n        for (int i = 0; i < this.buttonList.size(); ++i) {\n            Button guibutton = this.buttonList.get(i);\n            if (guibutton.mouseClicked(evt, isDoubleClick)) {\n                guibutton.playDownSound(Minecraft.getInstance().getSoundManager());\n                this.actionPerformed(guibutton);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    protected void actionPerformed(Button guibutton) {\n\n    }\n\n    public static enum Box {\n\n        LIGHT(0, 0),\n        DARK(0, 19);\n\n        private final int x, y;\n\n        private Box(int x, int y) {\n            this.x = x;\n            this.y = y;\n        }\n\n        public int getX() {\n            return this.x;\n        }\n\n        public int getY() {\n            return this.y;\n        }\n\n    }\n\n    public static class Base extends SubGuiBox {\n\n        private final int x, y, width, height;\n\n        public Base(Box type, int x, int y, int width, int height) {\n            super(type);\n            this.x = x;\n            this.y = y;\n            this.width = width;\n            this.height = height;\n        }\n\n        @Override\n        public int getX() {\n            return x;\n        }\n\n        @Override\n        public int getY() {\n            return y;\n        }\n\n        @Override\n        public int getWidth() {\n            return width;\n        }\n\n        @Override\n        public int getHeight() {\n            return height;\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n\n        }\n\n        @Override\n        public void tick() {\n\n        }\n\n        @Override\n        public boolean charTyped(CharacterEvent evt) {\n            return false;\n        }\n\n        @Override\n        public boolean keyPressed(KeyEvent evt) {\n            return false;\n        }\n\n        @Override\n        public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n            return super.mouseClicked(evt, isDoubleClick);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/gui/subgui/SubGuiHolder.java",
    "content": "package org.cyclops.integrateddynamics.core.client.gui.subgui;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGui;\n\nimport java.util.Set;\n\n/**\n * A subgui that itself can contain multiple {@link ISubGui} and delegates to them.\n * @author rubensworks\n */\npublic class SubGuiHolder implements ISubGui {\n\n    private final Set<ISubGui> subGuis = Sets.newTreeSet(new ISubGui.SubGuiComparator());\n\n    public void addSubGui(ISubGui subGui) {\n        subGuis.add(subGui);\n    }\n\n    public boolean removeSubGui(ISubGui subGui) {\n        return subGuis.remove(subGui);\n    }\n\n    public void clear() {\n        subGuis.clear();\n    }\n\n    protected Set<ISubGui> getSubGuis() {\n        return Sets.newHashSet(subGuis);\n    }\n\n    public boolean isEmpty() {\n        return subGuis.isEmpty();\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        for(ISubGui subGui : getSubGuis()) {\n            subGui.init(guiLeft, guiTop);\n        }\n    }\n\n    @Override\n    public void tick() {\n        for(ISubGui subGui : getSubGuis()) {\n            subGui.tick();\n        }\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        for(ISubGui subGui : getSubGuis()) {\n            subGui.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n        }\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        for(ISubGui subGui : getSubGuis()) {\n            subGui.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n        }\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        for(ISubGui subGui : getSubGuis()) {\n            if(subGui.charTyped(evt)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        for(ISubGui subGui : getSubGuis()) {\n            if(subGui.keyPressed(evt)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        for(ISubGui subGui : getSubGuis()) {\n            if (subGui.mouseClicked(evt, isDoubleClick)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/AspectVariableModelProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.client.resources.model.ResolvableModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.joml.Matrix4fc;\n\nimport java.util.Map;\n\n/**\n * Variable facadeModel provider for aspects.\n * @author rubensworks\n */\npublic class AspectVariableModelProvider implements IVariableModelProvider<BakedMapVariableModelProvider<IAspect>> {\n    @Override\n    public BakedMapVariableModelProvider<IAspect> bakeOverlayModels(ItemModel.BakingContext bakingContext, Matrix4fc matrix) {\n        Map<IAspect, ItemModel> bakedModels = Maps.newHashMap();\n        for(IAspect aspect : Aspects.REGISTRY.getAspects()) {\n            ItemModel.Unbaked unbakedModel = Aspects.REGISTRY.getClient().getAspectModel(aspect);\n            ItemModel bakedModel = unbakedModel.bake(bakingContext, matrix);\n            bakedModels.put(aspect, bakedModel);\n        }\n        return new BakedMapVariableModelProvider<>(bakedModels);\n    }\n\n    @Override\n    public void resolveDependencies(ResolvableModel.Resolver resolver) {\n        for(IAspect aspect : Aspects.REGISTRY.getAspects()) {\n            Aspects.REGISTRY.getClient().getAspectModel(aspect).resolveDependencies(resolver);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/BakedMapVariableModelProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\n\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * A baked facadeModel provider that maps keys to baked models.\n * @param <T> The key type.\n * @author rubensworks\n */\npublic class BakedMapVariableModelProvider<T> implements IVariableModelProvider.BakedModelProvider {\n    private final Map<T, ItemModel> bakedModels;\n\n    public BakedMapVariableModelProvider(Map<T, ItemModel> bakedModels) {\n        this.bakedModels = bakedModels;\n    }\n\n    public Map<T, ItemModel> getBakedModels() {\n        return bakedModels;\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        BakedMapVariableModelProvider<?> that = (BakedMapVariableModelProvider<?>) o;\n        return Objects.equals(bakedModels, that.bakedModels);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(bakedModels);\n    }\n\n    @Override\n    public String toString() {\n        return \"BakedMapVariableModelProvider(bakedModels=\" + bakedModels + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/BakedSingleVariableModelProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\n\nimport java.util.Objects;\n\n/**\n * A baked facadeModel provider that maps keys to baked models.\n * @author rubensworks\n */\npublic class BakedSingleVariableModelProvider implements IVariableModelProvider.BakedModelProvider {\n    private final ItemModel bakedModel;\n\n    public BakedSingleVariableModelProvider(ItemModel bakedModel) {\n        this.bakedModel = bakedModel;\n    }\n\n    public ItemModel getBakedModel() {\n        return bakedModel;\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        BakedSingleVariableModelProvider that = (BakedSingleVariableModelProvider) o;\n        return Objects.equals(bakedModel, that.bakedModel);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(bakedModel);\n    }\n\n    @Override\n    public String toString() {\n        return \"BakedSingleVariableModelProvider(bakedModel=\" + bakedModel + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/CustomModelException.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport net.minecraft.resources.Identifier;\n\n/**\n * Exception that can occur while loading custom models.\n * @author rubensworks\n */\npublic class CustomModelException extends RuntimeException {\n\n    public CustomModelException(Identifier modelLocation) {\n        super(String.format(\"The facadeModel %s could not be loaded.\", modelLocation));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/ItemModelCable.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.renderer.item.*;\nimport net.minecraft.client.resources.model.geometry.BakedQuad;\nimport net.minecraft.client.resources.model.geometry.QuadCollection;\nimport net.minecraft.world.entity.ItemOwner;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.client.model.DynamicItemAndBlockModel;\nimport org.cyclops.integrateddynamics.client.model.CableModel;\nimport org.cyclops.integrateddynamics.client.model.CableModelBase;\nimport org.jetbrains.annotations.Nullable;\nimport org.joml.Matrix4f;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic record ItemModelCable(DynamicItemAndBlockModel model) implements ItemModel {\n    @Override\n    public void update(ItemStackRenderState renderState, ItemStack stack, ItemModelResolver itemModelResolver, ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable ItemOwner entity, int seed) {\n        ModelRenderProperties modelRenderProperties = model.getModelRenderProperties();\n        List<BakedQuad> quadList = this.model.handleItemState(stack, level, entity);\n        QuadCollection.Builder quadBuilder = new QuadCollection.Builder();\n        for (BakedQuad quad : quadList) {\n            quadBuilder.addUnculledFace(quad);\n        }\n        new CuboidItemModelWrapper(List.of(), quadBuilder.build(), modelRenderProperties, new Matrix4f())\n                .update(renderState, stack, itemModelResolver, displayContext, level, entity, seed);\n    }\n\n    public static record Unbaked() implements ItemModel.Unbaked {\n        public static final MapCodec<ItemModelCable.Unbaked> MAP_CODEC = MapCodec.unit(new ItemModelCable.Unbaked());\n\n        @Override\n        public MapCodec<ItemModelCable.Unbaked> type() {\n            return MAP_CODEC;\n        }\n\n        @Override\n        public ItemModel bake(BakingContext bakingContext, org.joml.Matrix4fc matrix) {\n            CableModelBase.MODEL_BAKER = bakingContext.blockModelBaker(); // Yes, this is a hack...\n            return new ItemModelCable(new CableModel());\n        }\n\n        @Override\n        public void resolveDependencies(Resolver resolver) {\n            // Do nothing\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/ItemModelFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.client.color.item.Constant;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.renderer.item.*;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.ItemOwner;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.client.render.model.FacadeModel;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic record ItemModelFacade(FacadeModel facadeModel) implements ItemModel {\n\n    @Override\n    public void update(ItemStackRenderState itemStackRenderState, ItemStack itemStack, ItemModelResolver itemModelResolver, ItemDisplayContext itemDisplayContext, @Nullable ClientLevel clientLevel, @Nullable ItemOwner itemOwner, int seed) {\n        facadeModel.update(itemStackRenderState, itemStack, itemModelResolver, itemDisplayContext, clientLevel, itemOwner, seed);\n    }\n\n    public static record Unbaked(Identifier modelEmpty) implements ItemModel.Unbaked {\n        public static final MapCodec<ItemModelFacade.Unbaked> MAP_CODEC = RecordCodecBuilder.mapCodec(\n                instance -> instance.group(\n                                Identifier.CODEC.fieldOf(\"model_empty\").forGetter(ItemModelFacade.Unbaked::modelEmpty)\n                        )\n                        .apply(instance, ItemModelFacade.Unbaked::new)\n        );\n\n        @Override\n        public MapCodec<ItemModelFacade.Unbaked> type() {\n            return MAP_CODEC;\n        }\n\n        @Override\n        public ItemModel bake(BakingContext bakingContext, org.joml.Matrix4fc matrix) {\n            return new ItemModelFacade(new FacadeModel(\n                    new CuboidItemModelWrapper.Unbaked(modelEmpty, java.util.Optional.empty(), List.of(new Constant(-1))).bake(bakingContext, matrix),\n                    new ModelRenderProperties(false, null, bakingContext.blockModelBaker().getModel(modelEmpty).getTopTransforms())\n            ));\n        }\n\n        @Override\n        public void resolveDependencies(Resolver resolver) {\n            resolver.markDependency(modelEmpty);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/ItemModelVariableOverlay.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.client.renderer.item.ItemModelResolver;\nimport net.minecraft.client.renderer.item.ItemStackRenderState;\nimport net.minecraft.world.entity.ItemOwner;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\nimport org.jetbrains.annotations.Nullable;\nimport org.joml.Matrix4f;\nimport org.joml.Matrix4fc;\n\n/**\n * @author rubensworks\n */\npublic record ItemModelVariableOverlay(ItemModelVariableOverlays variableModelBaked) implements ItemModel {\n\n    @Override\n    public void update(ItemStackRenderState renderState, ItemStack stack, ItemModelResolver itemModelResolver, ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable ItemOwner entity, int seed) {\n        ItemModel model = this.variableModelBaked.getModelForItem(stack, level);\n        if (model != null) {\n            model.update(renderState, stack, itemModelResolver, displayContext, level, entity, seed);\n        }\n    }\n\n    public static record Unbaked() implements ItemModel.Unbaked {\n        public static final MapCodec<ItemModelVariableOverlay.Unbaked> MAP_CODEC = MapCodec.unit(new ItemModelVariableOverlay.Unbaked());\n\n        @Override\n        public MapCodec<ItemModelVariableOverlay.Unbaked> type() {\n            return MAP_CODEC;\n        }\n\n        @Override\n        public ItemModel bake(BakingContext bakingContext, Matrix4fc matrix) {\n            ItemModelVariableOverlays bakedModel = new ItemModelVariableOverlays();\n            Matrix4f matrixLocal = new Matrix4f(matrix);\n            float offset = 0;\n            for(IVariableModelProvider provider : VariableModelProviders.REGISTRY.getProviders()) {\n                offset += 0.01F;\n                bakedModel.setSubModels(provider, provider.bakeOverlayModels(bakingContext, matrixLocal.translate(0F, 0F, offset)));\n            }\n            return new ItemModelVariableOverlay(bakedModel);\n        }\n\n        @Override\n        public void resolveDependencies(Resolver resolver) {\n            for(IVariableModelProvider provider : VariableModelProviders.REGISTRY.getProviders()) {\n                provider.resolveDependencies(resolver);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/ItemModelVariableOverlays.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\n\n/**\n * Baked item facadeModel overlays for variables.\n * @author rubensworks\n */\npublic class ItemModelVariableOverlays implements IVariableModelBaked {\n\n    private final Map<IVariableModelProvider<?>, IVariableModelProvider.BakedModelProvider> subModels = Maps.newHashMap();\n\n    @Override\n    public <B extends IVariableModelProvider.BakedModelProvider> void setSubModels(IVariableModelProvider<B> provider, B subModels) {\n        this.subModels.put(provider, subModels);\n    }\n\n    @Override\n    public <B extends IVariableModelProvider.BakedModelProvider> B getSubModels(IVariableModelProvider<B> provider) {\n        return (B) this.subModels.get(provider);\n    }\n\n    @Nullable\n    public ItemModel getModelForItem(ItemStack itemStack, Level world) {\n        // Add variable type overlay\n        IVariableFacade variableFacade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(ValueDeseralizationContext.of(world == null ? Minecraft.getInstance().level : world), itemStack);\n        return variableFacade.getClient().getItemModelOverlay(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/SingleVariableModelProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport net.minecraft.client.color.item.Constant;\nimport net.minecraft.client.renderer.item.CuboidItemModelWrapper;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.client.resources.model.ResolvableModel;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\nimport org.joml.Matrix4fc;\n\nimport java.util.List;\n\n/**\n * Variable facadeModel provider for a single facadeModel.\n * @author rubensworks\n */\npublic class SingleVariableModelProvider implements IVariableModelProvider<BakedSingleVariableModelProvider> {\n\n    private ItemModel.Unbaked modelUnbaked;\n\n    public SingleVariableModelProvider(Identifier model) {\n        this.modelUnbaked = new CuboidItemModelWrapper.Unbaked(model, java.util.Optional.empty(), List.of(new Constant(-1)));\n    }\n\n    @Override\n    public BakedSingleVariableModelProvider bakeOverlayModels(ItemModel.BakingContext bakingContext, Matrix4fc matrix) {\n        return new BakedSingleVariableModelProvider(this.modelUnbaked.bake(bakingContext, matrix));\n    }\n\n    @Override\n    public void resolveDependencies(ResolvableModel.Resolver resolver) {\n        this.modelUnbaked.resolveDependencies(resolver);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/ValueTypeVariableModelProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.client.resources.model.ResolvableModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.joml.Matrix4fc;\n\nimport java.util.Map;\n\n/**\n * Variable facadeModel provider for value types.\n * @author rubensworks\n */\npublic class ValueTypeVariableModelProvider implements IVariableModelProvider<BakedMapVariableModelProvider<IValueType>> {\n    @Override\n    public BakedMapVariableModelProvider<IValueType> bakeOverlayModels(ItemModel.BakingContext bakingContext, Matrix4fc matrix) {\n        Map<IValueType, ItemModel> bakedModels = Maps.newHashMap();\n        for(IValueType valueType : ValueTypes.REGISTRY.getValueTypes()) {\n            try {\n                ItemModel.Unbaked unbakedModel = ValueTypes.REGISTRY.getClient().getValueTypeModel(valueType);\n                if(unbakedModel != null) {\n                    ItemModel bakedModel = unbakedModel.bake(bakingContext, matrix);\n                    bakedModels.put(valueType, bakedModel);\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n        return new BakedMapVariableModelProvider<>(bakedModels);\n    }\n\n    @Override\n    public void resolveDependencies(ResolvableModel.Resolver resolver) {\n        for(IValueType valueType : ValueTypes.REGISTRY.getValueTypes()) {\n            ValueTypes.REGISTRY.getClient().getValueTypeModel(valueType).resolveDependencies(resolver);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/VariableModelProviderRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProvider;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProviderRegistry;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Registry for {@link IVariableModelProvider}.\n * @author rubensworks\n */\npublic class VariableModelProviderRegistry implements IVariableModelProviderRegistry {\n\n    private static final VariableModelProviderRegistry INSTANCE = new VariableModelProviderRegistry();\n\n    private final List<IVariableModelProvider<?>> providers = Lists.newArrayList();\n\n    /**\n     * @return The unique instance.\n     */\n    public static VariableModelProviderRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <E extends IVariableModelProvider<B>, B extends IVariableModelProvider.BakedModelProvider> E addProvider(E provider) {\n        providers.add(provider);\n        return provider;\n    }\n\n    @Override\n    public List<IVariableModelProvider<? extends IVariableModelProvider.BakedModelProvider>> getProviders() {\n        return Collections.unmodifiableList(providers);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/client/model/VariableModelProviders.java",
    "content": "package org.cyclops.integrateddynamics.core.client.model;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelProviderRegistry;\n\n/**\n * Collection of variable facadeModel providers.\n * @author rubensworks\n */\npublic class VariableModelProviders {\n\n    public static final IVariableModelProviderRegistry REGISTRY = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableModelProviderRegistry.class);\n\n    public static final ValueTypeVariableModelProvider VALUETYPE = REGISTRY.addProvider(new ValueTypeVariableModelProvider());\n    public static final AspectVariableModelProvider ASPECT = REGISTRY.addProvider(new AspectVariableModelProvider());\n    public static final SingleVariableModelProvider PROXY = REGISTRY.addProvider(new SingleVariableModelProvider(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"customoverlay/proxy\")));\n    public static final SingleVariableModelProvider DELAY = REGISTRY.addProvider(new SingleVariableModelProvider(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"customoverlay/delay\")));\n\n    public static void load() {}\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/datastructure/MapWrapper.java",
    "content": "package org.cyclops.integrateddynamics.core.datastructure;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * A simple Map wrapper.\n * @author rubensworks\n */\npublic class MapWrapper<K, V> implements Map<K, V> {\n\n    private final Map<K, V> map;\n\n    public MapWrapper(Map<K, V> map) {\n        this.map = map;\n    }\n\n    @Override\n    public int size() {\n        return this.map.size();\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return this.map.isEmpty();\n    }\n\n    @Override\n    public boolean containsKey(Object key) {\n        return this.map.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(Object value) {\n        return this.map.containsValue(value);\n    }\n\n    @Override\n    public V get(Object key) {\n        return this.map.get(key);\n    }\n\n    @Override\n    public V put(K key, V value) {\n        return this.map.put(key, value);\n    }\n\n    @Override\n    public V remove(Object key) {\n        return this.map.remove(key);\n    }\n\n    @Override\n    public void putAll(Map<? extends K, ? extends V> m) {\n        this.map.putAll(m);\n    }\n\n    @Override\n    public void clear() {\n        this.map.clear();\n    }\n\n    @Override\n    public Set<K> keySet() {\n        return this.map.keySet();\n    }\n\n    @Override\n    public Collection<V> values() {\n        return this.map.values();\n    }\n\n    @Override\n    public Set<Entry<K, V>> entrySet() {\n        return this.map.entrySet();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/DelayVariableFacadeHandler.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IDelayVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.core.item.DelayVariableFacade;\n\n/**\n * Handler for delay variable facades.\n * @author rubensworks\n */\npublic class DelayVariableFacadeHandler implements IVariableFacadeHandler<IDelayVariableFacade> {\n\n    private static final IDelayVariableFacade INVALID_FACADE = new DelayVariableFacade(false, -1);\n    private static DelayVariableFacadeHandler _instance;\n\n    private DelayVariableFacadeHandler() {\n\n    }\n\n    public static DelayVariableFacadeHandler getInstance() {\n        if(_instance == null) _instance = new DelayVariableFacadeHandler();\n        return _instance;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"delay\");\n    }\n\n    @Override\n    public IDelayVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, int id, CompoundTag tag) {\n        if(!tag.contains(\"partId\")) {\n            return INVALID_FACADE;\n        }\n        return new DelayVariableFacade(id, tag.getInt(\"partId\").orElseThrow());\n    }\n\n    @Override\n    public void setVariableFacade(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag, IDelayVariableFacade variableFacade) {\n        tag.putInt(\"partId\", variableFacade.getProxyId());\n    }\n\n    @Override\n    public boolean isInstance(IVariableFacade variableFacade) {\n        return variableFacade instanceof IDelayVariableFacade;\n    }\n\n    @Override\n    public boolean isInstance(IVariable<?> variable) {\n        return variable instanceof IVariable;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/IOperatorValuePropagator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\n\n/**\n * Used for forwarding values to a next propagator.\n * @author rubensworks\n */\npublic interface IOperatorValuePropagator<I, O> {\n\n    public O getOutput(I input) throws EvaluationException;\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/InventoryVariableEvaluator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.function.Supplier;\n\n/**\n * A convenience holder class for getting variables from variable cards in a certain inventory slot.\n * @param <V> The variable value type\n * @author rubensworks\n */\npublic class InventoryVariableEvaluator<V extends IValue> implements IVariableFacade.IValidator {\n\n    private final IVariableFacadeHandlerRegistry handler = IntegratedDynamics._instance.getRegistryManager()\n            .getRegistry(IVariableFacadeHandlerRegistry.class);\n    private final Container inventory;\n    private final int slot;\n    private final Supplier<ValueDeseralizationContext> valueDeseralizationContext;\n    private final IValueType containingValueType;\n\n    private IVariableFacade variableStored = null;\n    private List<MutableComponent> errors = Lists.newLinkedList();\n\n    public InventoryVariableEvaluator(Container inventory, int slot, Supplier<ValueDeseralizationContext> valueDeseralizationContext, IValueType<V> containingValueType) {\n        this.inventory = inventory;\n        this.slot = slot;\n        this.valueDeseralizationContext = valueDeseralizationContext;\n        this.containingValueType = containingValueType;\n    }\n\n    public InventoryVariableEvaluator(Container inventory, int slot, ValueDeseralizationContext valueDeseralizationContext, IValueType<V> containingValueType) {\n        this(inventory, slot, () -> valueDeseralizationContext, containingValueType);\n    }\n\n    /**\n     * @return If the configured slot has an item.\n     */\n    public boolean hasVariable() {\n        return !inventory.getItem(slot).isEmpty();\n    }\n\n    /**\n     * Refresh the variable reference by checking the inventory,\n     * and validating the containing variable.\n     * @param network The network.\n     * @param sendVariablesUpdateEvent If a {@link VariableContentsUpdatedEvent} event must be sent\n     *                                 if the variable has changed.\n     */\n    public void refreshVariable(@Nullable INetwork network, boolean sendVariablesUpdateEvent) {\n        IPartNetwork partNetwork = NetworkHelpers.getPartNetwork(network).orElse(null);\n\n        int lastVariabledId = this.variableStored == null ? -1 : this.variableStored.getId();\n        int variableId = -1;\n        if (!inventory.getItem(slot).isEmpty() && NetworkHelpers.shouldWork()) {\n            // Update proxy input\n            ItemStack itemStack = inventory.getItem(slot);\n            this.variableStored = handler.handle(valueDeseralizationContext.get(), itemStack);\n            if(this.variableStored != null) {\n                variableId = this.variableStored.getId();\n            }\n        } else {\n            this.variableStored = null;\n        }\n\n        clearErrors();\n        if (partNetwork == null) {\n            addError(Component.translatable(L10NValues.GENERAL_ERROR_NONETWORK));\n        } else if (this.variableStored != null) {\n            preValidate();\n            try {\n                variableStored.validate(network, partNetwork, this, containingValueType);\n            } catch (IllegalArgumentException e) {\n                addError(Component.translatable(e.getMessage()));\n            }\n        }\n        if(sendVariablesUpdateEvent && partNetwork != null && lastVariabledId != variableId) {\n            network.getEventBus().post(new VariableContentsUpdatedEvent(network));\n        }\n    }\n\n    @Nullable\n    public IVariable<V> getVariable(INetwork network) {\n        return getVariable(network, NetworkHelpers.getPartNetworkChecked(network));\n    }\n\n    @Nullable\n    public IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork) {\n        if(getVariableFacade() == null || !getErrors().isEmpty()) return null;\n        try {\n            return getVariableFacade().getVariable(network, partNetwork);\n        } catch (IllegalArgumentException e) {\n            addError(Component.translatable(e.getMessage()));\n            return null;\n        }\n    }\n\n    public IVariableFacade getVariableFacade() {\n        return variableStored;\n    }\n\n    protected void preValidate() {\n\n    }\n\n    public void clearErrors() {\n        this.errors.clear();\n        onErrorsChanged();\n    }\n\n    public void setErrors(List<MutableComponent> errors) {\n        this.errors = errors;\n        onErrorsChanged();\n    }\n\n    public List<MutableComponent> getErrors() {\n        return errors;\n    }\n\n    @Override\n    public void addError(MutableComponent error) {\n        errors.add(error);\n        onErrorsChanged();\n    }\n\n    public void onErrorsChanged() {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/OperatorBuilders.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate;\n\nimport com.google.common.collect.Iterables;\nimport com.google.common.collect.Lists;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.SoundType;\nimport net.neoforged.fml.ModContainer;\nimport net.neoforged.fml.ModList;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.capabilities.ItemCapability;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.build.OperatorBuilder;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.IterativeFunction;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.OperatorBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\n\nimport javax.annotation.Nullable;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Collection of operator builders.\n * @author rubensworks\n */\npublic class OperatorBuilders {\n\n    // --------------- Logical builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> LOGICAL = OperatorBuilder.forType(ValueTypes.BOOLEAN).appendKind(\"logical\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> LOGICAL_1_PREFIX = LOGICAL.inputTypes(1, ValueTypes.BOOLEAN).renderPattern(IConfigRenderPattern.PREFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> LOGICAL_2 = LOGICAL.inputTypes(2, ValueTypes.BOOLEAN).renderPattern(IConfigRenderPattern.INFIX);\n\n    // --------------- Value propagators ---------------\n    public static final IOperatorValuePropagator<Integer, IValue> PROPAGATOR_INTEGER_VALUE = ValueTypeInteger.ValueInteger::of;\n    public static final IOperatorValuePropagator<Long, IValue> PROPAGATOR_LONG_VALUE = ValueTypeLong.ValueLong::of;\n    public static final IOperatorValuePropagator<Boolean, IValue> PROPAGATOR_BOOLEAN_VALUE = ValueTypeBoolean.ValueBoolean::of;\n    public static final IOperatorValuePropagator<Double, IValue> PROPAGATOR_DOUBLE_VALUE = ValueTypeDouble.ValueDouble::of;\n    public static final IOperatorValuePropagator<String, IValue> PROPAGATOR_STRING_VALUE = ValueTypeString.ValueString::of;\n    public static final IOperatorValuePropagator<Optional<Tag>, IValue> PROPAGATOR_NBT_VALUE = ValueTypeNbt.ValueNbt::of;\n    public static final IOperatorValuePropagator<Optional<CompoundTag>, IValue> PROPAGATOR_NBT_COMPOUND_VALUE = opt -> ValueTypeNbt.ValueNbt.of(opt.map(t -> (Tag) t));\n    public static final IOperatorValuePropagator<Identifier, ValueTypeString.ValueString> PROPAGATOR_RESOURCELOCATION_MODNAME = resourceLocation -> {\n        String modId = resourceLocation.getNamespace();\n        Optional<? extends ModContainer> mod = ModList.get().getModContainerById(modId);\n        String modName = mod\n                .map(modContainer -> modContainer.getModInfo().getDisplayName())\n                .orElse(\"Minecraft\");\n        return ValueTypeString.ValueString.of(modName);\n    };\n\n    // --------------- Arithmetic builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ARITHMETIC = OperatorBuilder.forType(ValueTypes.CATEGORY_NUMBER).appendKind(\"arithmetic\").conditionalOutputTypeDeriver((operator, input) -> {\n        IValueType[] original = ValueHelpers.from(input);\n        IValueTypeNumber[] types = new IValueTypeNumber[original.length];\n        for(int i = 0; i < original.length; i++) {\n            if (original[i].isCategory()) {\n                // This avoids a class-cast exception in cases where we don't know the exact type.\n                return original[i];\n            }\n            types[i] = (IValueTypeNumber) original[i];\n        }\n        return ValueTypes.CATEGORY_NUMBER.getLowestType(types);\n    });\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ARITHMETIC_1_SUFFIX = ARITHMETIC.inputTypes(1, ValueTypes.CATEGORY_NUMBER).renderPattern(IConfigRenderPattern.SUFFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ARITHMETIC_2 = ARITHMETIC.inputTypes(2, ValueTypes.CATEGORY_NUMBER).renderPattern(IConfigRenderPattern.INFIX);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ARITHMETIC_2_PREFIX = ARITHMETIC.inputTypes(2, ValueTypes.CATEGORY_NUMBER).renderPattern(IConfigRenderPattern.PREFIX_2);\n\n    // --------------- Integer builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INTEGER = OperatorBuilder.forType(ValueTypes.INTEGER).appendKind(\"integer\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INTEGER_1_SUFFIX = INTEGER.inputTypes(1, ValueTypes.INTEGER).renderPattern(IConfigRenderPattern.SUFFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INTEGER_2 = INTEGER.inputTypes(2, ValueTypes.INTEGER).renderPattern(IConfigRenderPattern.INFIX);\n\n    // --------------- Relational builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> RELATIONAL = OperatorBuilder.forType(ValueTypes.BOOLEAN).appendKind(\"relational\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> RELATIONAL_2 = RELATIONAL.inputTypes(2, ValueTypes.INTEGER).renderPattern(IConfigRenderPattern.INFIX);\n\n    // --------------- Binary builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> BINARY = OperatorBuilder.forType(ValueTypes.INTEGER).appendKind(\"binary\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> BINARY_1_PREFIX = BINARY.inputTypes(1, ValueTypes.INTEGER).renderPattern(IConfigRenderPattern.PREFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> BINARY_2 = BINARY.inputTypes(2, ValueTypes.INTEGER).renderPattern(IConfigRenderPattern.INFIX);\n\n    // --------------- String builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> STRING = OperatorBuilder.forType(ValueTypes.STRING).appendKind(\"string\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> STRING_1_PREFIX = STRING.inputTypes(1, ValueTypes.STRING).renderPattern(IConfigRenderPattern.PREFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> STRING_2 = STRING.inputTypes(2, ValueTypes.STRING).renderPattern(IConfigRenderPattern.INFIX);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> STRING_2_LONG = STRING.inputTypes(2, ValueTypes.STRING).renderPattern(IConfigRenderPattern.INFIX_LONG);\n\n    // --------------- Double builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> DOUBLE = OperatorBuilder.forType(ValueTypes.DOUBLE).appendKind(\"double\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> DOUBLE_1_PREFIX = DOUBLE.inputTypes(1, ValueTypes.DOUBLE).renderPattern(IConfigRenderPattern.PREFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> DOUBLE_2 = DOUBLE.inputTypes(2, ValueTypes.DOUBLE).renderPattern(IConfigRenderPattern.INFIX);\n\n    // --------------- Number builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NUMBER = OperatorBuilder.forType(ValueTypes.CATEGORY_NUMBER).appendKind(\"number\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NUMBER_1_PREFIX = NUMBER.inputTypes(1, ValueTypes.CATEGORY_NUMBER).renderPattern(IConfigRenderPattern.PREFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NUMBER_1_LONG = NUMBER.inputTypes(1, ValueTypes.CATEGORY_NUMBER).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n\n    // --------------- Nullable builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NULLABLE = OperatorBuilder.forType(ValueTypes.CATEGORY_NULLABLE).appendKind(\"general\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NULLABLE_1_PREFIX = NULLABLE.inputTypes(1, ValueTypes.CATEGORY_NULLABLE).renderPattern(IConfigRenderPattern.PREFIX_1);\n\n    // --------------- List builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> LIST = OperatorBuilder.forType(ValueTypes.LIST).appendKind(\"list\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> LIST_1_PREFIX = LIST.inputTypes(1, ValueTypes.LIST).renderPattern(IConfigRenderPattern.PREFIX_1);\n\n    // --------------- Block builders ---------------\n    public static final OperatorBuilder BLOCK = OperatorBuilder.forType(ValueTypes.OBJECT_BLOCK).appendKind(\"block\");\n    public static final OperatorBuilder BLOCK_1_SUFFIX_LONG = BLOCK.inputTypes(1, ValueTypes.OBJECT_BLOCK).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder BLOCK_INFIX_VERYLONG = BLOCK.inputTypes(2, ValueTypes.OBJECT_BLOCK).renderPattern(IConfigRenderPattern.INFIX_VERYLONG);\n    public static final IOperatorValuePropagator<OperatorBase.SafeVariablesGetter, Optional<SoundType>> BLOCK_SOUND = input -> {\n        ValueObjectTypeBlock.ValueBlock block = input.getValue(0, ValueTypes.OBJECT_BLOCK);\n        if(block.getRawValue().isPresent()) {\n            return Optional.of(block.getRawValue().get().getSoundType());\n        }\n        return Optional.empty();\n    };\n\n    // --------------- ItemStack builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK = OperatorBuilder.forType(ValueTypes.OBJECT_ITEMSTACK).appendKind(\"itemstack\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK_1_PREFIX_LONG = ITEMSTACK.inputTypes(1, ValueTypes.OBJECT_ITEMSTACK).renderPattern(IConfigRenderPattern.PREFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK_1_SUFFIX_LONG = ITEMSTACK.inputTypes(1, ValueTypes.OBJECT_ITEMSTACK).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK_2 = ITEMSTACK.inputTypes(2, ValueTypes.OBJECT_ITEMSTACK).renderPattern(IConfigRenderPattern.INFIX);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK_2_LONG = ITEMSTACK.inputTypes(2, ValueTypes.OBJECT_ITEMSTACK).renderPattern(IConfigRenderPattern.INFIX_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK_1_INTEGER_1 = ITEMSTACK.inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.INTEGER}).renderPattern(IConfigRenderPattern.INFIX);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ITEMSTACK_3 = ITEMSTACK.inputTypes(3, ValueTypes.OBJECT_ITEMSTACK).renderPattern(IConfigRenderPattern.PREFIX_3_LONG);\n    public static final IterativeFunction.PrePostBuilder<ItemStack, IValue> FUNCTION_ITEMSTACK = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueObjectTypeItemStack.ValueItemStack value = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                return value.getRawValue();\n            });\n    public static final IterativeFunction.PrePostBuilder<ItemStack, Integer> FUNCTION_ITEMSTACK_TO_INT =\n            FUNCTION_ITEMSTACK.appendPost(PROPAGATOR_INTEGER_VALUE);\n    public static final IterativeFunction.PrePostBuilder<ItemStack, Long> FUNCTION_ITEMSTACK_TO_LONG =\n            FUNCTION_ITEMSTACK.appendPost(PROPAGATOR_LONG_VALUE);\n    public static final IterativeFunction.PrePostBuilder<ItemStack, Boolean> FUNCTION_ITEMSTACK_TO_BOOLEAN =\n            FUNCTION_ITEMSTACK.appendPost(PROPAGATOR_BOOLEAN_VALUE);\n\n    public static final IterativeFunction.PrePostBuilder<EnergyHandler, IValue> FUNCTION_ENERGYSTORAGEITEM = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueObjectTypeItemStack.ValueItemStack a = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                if(!a.getRawValue().isEmpty()) {\n                    return ItemAccess.forStack(a.getRawValue()).getCapability(Capabilities.Energy.ITEM);\n                }\n                return null;\n            });\n    public static final IterativeFunction.PrePostBuilder<EnergyHandler, Long> FUNCTION_CONTAINERITEM_TO_LONG =\n            FUNCTION_ENERGYSTORAGEITEM.appendPost(OperatorBuilders.PROPAGATOR_LONG_VALUE);\n    public static final IterativeFunction.PrePostBuilder<EnergyHandler, Boolean> FUNCTION_CONTAINERITEM_TO_BOOLEAN =\n            FUNCTION_ENERGYSTORAGEITEM.appendPost(OperatorBuilders.PROPAGATOR_BOOLEAN_VALUE);\n\n    // --------------- Entity builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ENTITY = OperatorBuilder.forType(ValueTypes.OBJECT_ENTITY).appendKind(\"entity\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ENTITY_1_SUFFIX = ENTITY.inputTypes(1, ValueTypes.OBJECT_ENTITY).renderPattern(IConfigRenderPattern.SUFFIX_1);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ENTITY_1_SUFFIX_LONG = ENTITY.inputTypes(1, ValueTypes.OBJECT_ENTITY).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> ENTITY_1_ITEMSTACK_1 = ENTITY.inputTypes(new IValueType[]{ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK}).renderPattern(IConfigRenderPattern.INFIX_LONG);\n    public static final IterativeFunction.PrePostBuilder<Entity, IValue> FUNCTION_ENTITY = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueObjectTypeEntity.ValueEntity a = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                return a.getRawValue().isPresent() ? a.getRawValue().get() : null;\n            });\n    public static final IterativeFunction.PrePostBuilder<Entity, Double> FUNCTION_ENTITY_TO_DOUBLE =\n            FUNCTION_ENTITY.appendPost(PROPAGATOR_DOUBLE_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Entity, Boolean> FUNCTION_ENTITY_TO_BOOLEAN =\n            FUNCTION_ENTITY.appendPost(PROPAGATOR_BOOLEAN_VALUE);\n\n    // --------------- FluidStack builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> FLUIDSTACK = OperatorBuilder.forType(ValueTypes.OBJECT_FLUIDSTACK).appendKind(\"fluidstack\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> FLUIDSTACK_1_SUFFIX_LONG = FLUIDSTACK.inputTypes(1, ValueTypes.OBJECT_FLUIDSTACK).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> FLUIDSTACK_2 = FLUIDSTACK.inputTypes(2, ValueTypes.OBJECT_FLUIDSTACK).renderPattern(IConfigRenderPattern.INFIX);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> FLUIDSTACK_2_LONG = FLUIDSTACK.inputTypes(2, ValueTypes.OBJECT_FLUIDSTACK).renderPattern(IConfigRenderPattern.INFIX_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> FLUIDSTACK_3 = ITEMSTACK.inputTypes(3, ValueTypes.OBJECT_FLUIDSTACK).renderPattern(IConfigRenderPattern.PREFIX_3_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> FLUIDSTACK_1_PREFIX_LONG = FLUIDSTACK.inputTypes(1, ValueTypes.OBJECT_FLUIDSTACK).renderPattern(IConfigRenderPattern.PREFIX_1_LONG);\n    public static final IterativeFunction.PrePostBuilder<FluidStack, IValue> FUNCTION_FLUIDSTACK = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueObjectTypeFluidStack.ValueFluidStack a = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                return a.getRawValue();\n            });\n    public static final IterativeFunction.PrePostBuilder<FluidStack, Integer> FUNCTION_FLUIDSTACK_TO_INT =\n            FUNCTION_FLUIDSTACK.appendPost(PROPAGATOR_INTEGER_VALUE);\n    public static final IterativeFunction.PrePostBuilder<FluidStack, Boolean> FUNCTION_FLUIDSTACK_TO_BOOLEAN =\n            FUNCTION_FLUIDSTACK.appendPost(PROPAGATOR_BOOLEAN_VALUE);\n\n    // --------------- Operator builders ---------------\n    public static final IterativeFunction.PrePostBuilder<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue> FUNCTION_OPERATOR_TAKE_OPERATOR = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                IOperator innerOperator = input.getValue(0, ValueTypes.OPERATOR).getRawValue();\n                if (input.getVariables().length > 1) {\n                    if (innerOperator.getRequiredInputLength() == 1) {\n                        IValue applyingValue = input.getValue(1);\n                        MutableComponent error = innerOperator.validateTypes(new IValueType[]{applyingValue.getType()});\n                        if (error != null) {\n                            throw new EvaluationException(error);\n                        }\n                    } else if (innerOperator.getRequiredInputLength() > 0) {\n                        if (!ValueHelpers.correspondsTo(input.getVariables()[1].getType(), innerOperator.getInputTypes()[0])) {\n                            MutableComponent error = Component.translatable(L10NValues.OPERATOR_ERROR_WRONGCURRYINGTYPE,\n                                    Component.translatable(innerOperator.getTranslationKey()),\n                                    Component.translatable(input.getVariables()[1].getType().getTranslationKey()),\n                                    0,\n                                    Component.translatable(innerOperator.getInputTypes()[0].getTranslationKey())\n                            );\n                            throw new EvaluationException(error);\n                        }\n                    }\n                }\n                return Pair.<IOperator, OperatorBase.SafeVariablesGetter>of(innerOperator,\n                        new OperatorBase.SafeVariablesGetter.Shifted(1, input.getVariables()));\n            });\n    public static final IterativeFunction.PrePostBuilder<IOperator, IValue> FUNCTION_ONE_OPERATOR = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> getSafeOperator(input.getValue(0, ValueTypes.OPERATOR), ValueTypes.CATEGORY_ANY));\n    public static final IterativeFunction.PrePostBuilder<IOperator, IValue> FUNCTION_ONE_PREDICATE = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> getSafePredictate(input.getValue(0, ValueTypes.OPERATOR)));\n    public static final IterativeFunction.PrePostBuilder<Pair<IOperator, IOperator>, IValue> FUNCTION_TWO_OPERATORS = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                IOperator second = getSafeOperator(input.getValue(1, ValueTypes.OPERATOR), ValueTypes.CATEGORY_ANY);\n                IValueType[] secondInputs = second.getInputTypes();\n                if(secondInputs.length < 1) {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.OPERATOR_ERROR_OPERATORPARAMWRONGINPUTLENGTH,\n                            1, second.getLocalizedNameFull(), secondInputs.length));\n                }\n                IValueType secondInputType = secondInputs[0];\n                if (ValueHelpers.correspondsTo(secondInputType, ValueTypes.OPERATOR)) {\n                    secondInputType = ValueTypes.CATEGORY_ANY;\n                }\n                IOperator first = getSafeOperator(input.getValue(0, ValueTypes.OPERATOR), secondInputType);\n                return Pair.of(first, second);\n            });\n    public static final IterativeFunction.PrePostBuilder<Pair<IOperator, IOperator>, IValue> FUNCTION_TWO_PREDICATES = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                IOperator first = getSafePredictate(input.getValue(0, ValueTypes.OPERATOR));\n                IOperator second = getSafePredictate(input.getValue(1, ValueTypes.OPERATOR));\n                return Pair.of(first, second);\n            });\n    public static final IterativeFunction.PrePostBuilder<Triple<IOperator, IOperator, IOperator>, IValue> FUNCTION_THREE_OPERATORS = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                IOperator third = getSafeOperator(input.getValue(2, ValueTypes.OPERATOR), ValueTypes.CATEGORY_ANY);\n                IValueType<?>[] types = third.getInputTypes();\n                if(types.length < 2) {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.OPERATOR_ERROR_OPERATORPARAMWRONGINPUTLENGTH,\n                            2, third.getLocalizedNameFull(), types.length));\n                }\n                IValueType<?> firstOutputType = types[0];\n                IValueType<?> secondOutputType = types[1];\n                if (ValueHelpers.correspondsTo(firstOutputType, ValueTypes.OPERATOR)) {\n                    firstOutputType = ValueTypes.CATEGORY_ANY;\n                }\n                if (ValueHelpers.correspondsTo(secondOutputType, ValueTypes.OPERATOR)) {\n                    secondOutputType = ValueTypes.CATEGORY_ANY;\n                }\n                IOperator first = getSafeOperator(input.getValue(0, ValueTypes.OPERATOR), firstOutputType);\n                IOperator second = getSafeOperator(input.getValue(1, ValueTypes.OPERATOR), secondOutputType);\n                return Triple.of(first, second, third);\n            });\n    public static final IterativeFunction.PrePostBuilder<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue> FUNCTION_OPERATOR_TAKE_OPERATOR_LIST = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueTypeOperator.ValueOperator valueOperator = input.getValue(0, ValueTypes.OPERATOR);\n                IOperator innerOperator = valueOperator.getRawValue();\n                input.getValue(1, ValueTypes.LIST); // To trigger exception on invalid type\n                return Pair.<IOperator, OperatorBase.SafeVariablesGetter>of(innerOperator,\n                        new OperatorBase.SafeVariablesGetter.Shifted(1, input.getVariables()));\n            });\n    /**\n     * Corresponds to {@link ValueHelpers#evaluateOperator(IOperator, IVariable[])}.\n     */\n    public static OperatorBuilder.IConditionalOutputTypeDeriver OPERATOR_CONDITIONAL_OUTPUT_DERIVER = (operator, variablesAll) -> {\n        try {\n            IValue value = variablesAll[0].getValue();\n            // In some cases, validation can succeed because of parameters being ANY.\n            // In this case, return a dummy type.\n            if (!(value instanceof ValueTypeOperator.ValueOperator)) {\n                return ValueTypes.CATEGORY_ANY;\n            }\n            IOperator innerOperator = ((ValueTypeOperator.ValueOperator) value).getRawValue();\n            IVariable[] variables = ArrayUtils.subarray(variablesAll, 1, variablesAll.length);\n            int requiredLength = innerOperator.getRequiredInputLength();\n            if (requiredLength == variables.length) {\n                Component error = innerOperator.validateTypes(ValueHelpers.from(variables));\n                if (error != null) {\n                    return innerOperator.getOutputType();\n                }\n                return innerOperator.getConditionalOutputType(variables);\n            } else {\n                if (variables.length > requiredLength) { // We have MORE variables as input than the operator accepts\n                    IVariable[] acceptableVariables = ArrayUtils.subarray(variables, 0, requiredLength);\n                    IVariable[] remainingVariables = ArrayUtils.subarray(variables, requiredLength, variables.length);\n\n                    // Pass all required variables to the operator, and forward all remaining ones to the resulting operator\n                    IValue result = ValueHelpers.evaluateOperator(innerOperator, acceptableVariables);\n\n                    // Error if the result is NOT an operator\n                    if (result.getType() != ValueTypes.OPERATOR) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_CURRYINGOVERFLOW,\n                                Component.translatable(innerOperator.getTranslationKey()),\n                                requiredLength,\n                                variables.length,\n                                Component.translatable(result.getType().getTranslationKey())));\n                    }\n\n                    // Pass all remaining variables to the resulting operator\n                    IOperator nextOperator = ((ValueTypeOperator.ValueOperator) result).getRawValue();\n                    Component error = nextOperator.validateTypes(ValueHelpers.from(remainingVariables));\n                    if (error != null) {\n                        return nextOperator.getOutputType();\n                    }\n                    return nextOperator.getConditionalOutputType(remainingVariables);\n                } else {\n                    return ValueTypes.OPERATOR;\n                }\n            }\n        } catch (EvaluationException e) {\n            return ValueTypes.CATEGORY_ANY;\n        }\n    };\n    /**\n     * Corresponds to {@link ValueHelpers#evaluateOperator(IOperator, IVariable[])}, but with the input variable being a list.\n     */\n    public static OperatorBuilder.IConditionalOutputTypeDeriver OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST = (operator, variablesIn) -> {\n        try {\n            // Get second param as list\n            IValue valueList = variablesIn[1].getValue();\n            // In some cases, validation can succeed because of parameters being ANY.\n            // In this case, return a dummy type.\n            if (!(valueList instanceof ValueTypeList.ValueList)) {\n                return ValueTypes.CATEGORY_ANY;\n            }\n            IValueTypeListProxy listProxy = ((ValueTypeList.ValueList) valueList).getRawValue();\n\n            // Expand the list to a variable array, with variablesIn[0] prepended (the operator to apply).\n            IVariable[] variablesExpanded = Stream.concat(\n                    Stream.of(variablesIn[0]),\n                    Stream.<IValue>of(Iterables.toArray(listProxy, IValue.class))\n                        .map(Variable::new)\n            ).toArray(IVariable[]::new);\n\n            return OPERATOR_CONDITIONAL_OUTPUT_DERIVER.getConditionalOutputType(operator, variablesExpanded);\n        } catch (EvaluationException e) {\n            return ValueTypes.CATEGORY_ANY;\n        }\n    };\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> OPERATOR = OperatorBuilder\n            .forType(ValueTypes.OPERATOR).appendKind(\"operator\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> OPERATOR_2_INFIX_LONG = OPERATOR\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY})\n            .renderPattern(IConfigRenderPattern.INFIX);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> OPERATOR_1_PREFIX_LONG = OPERATOR\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})\n            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG);\n\n    // --------------- String builders ---------------\n\n    public static final IterativeFunction.PrePostBuilder<Identifier, IValue> FUNCTION_STRING_TO_RESOURCE_LOCATION = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueTypeString.ValueString a = input.getValue(0, ValueTypes.STRING);\n                return ValueHelpers.createIdentifierInEvaluation(a.getRawValue());\n            });\n\n    // --------------- Operator helpers ---------------\n\n    /**\n     * Get the operator from a value in a safe manner.\n     * @param value The operator value.\n     * @param expectedOutput The expected output value type.\n     * @return The operator.\n     * @throws EvaluationException If the operator is not a predicate.\n     */\n    public static IOperator getSafeOperator(ValueTypeOperator.ValueOperator value, IValueType expectedOutput) throws EvaluationException {\n        IOperator operator = value.getRawValue();\n        if (!ValueHelpers.correspondsTo(operator.getOutputType(), expectedOutput)) {\n            MutableComponent error = Component.translatable(L10NValues.OPERATOR_ERROR_ILLEGALPROPERY,\n                    Component.translatable(expectedOutput.getTranslationKey()),\n                    Component.translatable(operator.getOutputType().getTranslationKey()),\n                    operator.getLocalizedNameFull());\n            throw new EvaluationException(error);\n        }\n        return operator;\n    }\n\n    /**\n     * Get the predicate from a value in a safe manner.\n     * It is expected that the operator returns a boolean.\n     * @param value The operator value.\n     * @return The operator.\n     * @throws EvaluationException If the operator is not a predicate.\n     */\n    public static IOperator getSafePredictate(ValueTypeOperator.ValueOperator value) throws EvaluationException {\n        return getSafeOperator(value, ValueTypes.BOOLEAN);\n    }\n\n    /**\n     * Create a type validator for operator operator type validators.\n     * @param expectedSubTypes The expected types that must be present in the operator (not including the first\n     *                         operator type itself.\n     * @return The type validator instance.\n     */\n    public static OperatorBuilder.ITypeValidator createOperatorTypeValidator(final IValueType<?>... expectedSubTypes) {\n        final int subOperatorLength = expectedSubTypes.length;\n        final Component expected = Component.translatable(\n                org.cyclops.integrateddynamics.core.helper.Helpers.createPatternOfLength(subOperatorLength), (Object[]) ValueHelpers.from(expectedSubTypes));\n        return (operator, input) -> {\n            if (input.length == 0 || !ValueHelpers.correspondsTo(input[0], ValueTypes.OPERATOR)) {\n                String givenName = input.length == 0 ? \"null\" : input[0].getTranslationKey();\n                return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDOPERATOROPERATOR,\n                        0, givenName);\n            }\n            if (input.length != subOperatorLength + 1) {\n                IValueType<?>[] operatorInputs = Arrays.copyOfRange(input, 1, input.length);\n                Component given = Component.translatable(\n                        org.cyclops.integrateddynamics.core.helper.Helpers.createPatternOfLength(operatorInputs.length), (Object[]) ValueHelpers.from(operatorInputs));\n                return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDOPERATORSIGNATURE,\n                        expected, given);\n            }\n            return null;\n        };\n    }\n\n    // --------------- NBT builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NBT = OperatorBuilder.forType(ValueTypes.NBT).appendKind(\"nbt\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NBT_1_PREFIX_LONG = NBT.inputTypes(ValueTypes.NBT).renderPattern(IConfigRenderPattern.PREFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NBT_1_SUFFIX_LONG = NBT.inputTypes(ValueTypes.NBT).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NBT_2 = NBT.inputTypes(ValueTypes.NBT, ValueTypes.STRING).renderPattern(IConfigRenderPattern.INFIX_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NBT_2_NBT = NBT.inputTypes(ValueTypes.NBT, ValueTypes.NBT).renderPattern(IConfigRenderPattern.INFIX_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> NBT_3 = NBT.inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING).output(ValueTypes.NBT).renderPattern(IConfigRenderPattern.INFIX_2_LONG);\n\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, IValue> FUNCTION_NBT = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueTypeNbt.ValueNbt value = input.getValue(0, ValueTypes.NBT);\n                return value.getRawValue();\n            });\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, IValue> FUNCTION_NBT_COMPOUND_ENTRY = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueTypeNbt.ValueNbt valueNbt = input.getValue(0, ValueTypes.NBT);\n                ValueTypeString.ValueString valueString = input.getValue(1, ValueTypes.STRING);\n                return valueNbt.getRawValue()\n                        .filter(tag -> tag instanceof CompoundTag)\n                        .map(tag -> ((CompoundTag) tag).get(valueString.getRawValue()));\n            });\n    public static final IterativeFunction.PrePostBuilder<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, IValue> FUNCTION_NBT_COPY_FOR_VALUE = IterativeFunction.PrePostBuilder.begin()\n            .appendPre(input -> {\n                ValueTypeNbt.ValueNbt valueNbt = input.getValue(0, ValueTypes.NBT);\n                ValueTypeString.ValueString valueString = input.getValue(1, ValueTypes.STRING);\n                return Triple.of(valueNbt.getRawValue()\n                                .filter(t -> t instanceof CompoundTag)\n                                .map(t -> (CompoundTag) t)\n                                .map(CompoundTag::copy), valueString.getRawValue(),\n                        new OperatorBase.SafeVariablesGetter.Shifted(2, input.getVariables()));\n            });\n\n    public static final IOperatorValuePropagator<Optional<Tag>, Optional<CompoundTag>> PROPAGATOR_NBT_COMPOUND = opt -> opt\n            .filter(t -> t instanceof CompoundTag)\n            .map(t -> (CompoundTag) t);\n\n    public static final IterativeFunction.PrePostBuilder<Optional<CompoundTag>, IValue> FUNCTION_NBT_COMPOUND = FUNCTION_NBT.appendPre(PROPAGATOR_NBT_COMPOUND);\n\n    public static final IterativeFunction.PrePostBuilder<Optional<CompoundTag>, Integer> FUNCTION_NBT_COMPOUND_TO_INT =\n            FUNCTION_NBT_COMPOUND.appendPost(PROPAGATOR_INTEGER_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<CompoundTag>, Boolean> FUNCTION_NBT_COMPOUND_TO_BOOLEAN =\n            FUNCTION_NBT_COMPOUND.appendPost(PROPAGATOR_BOOLEAN_VALUE);\n\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Integer> FUNCTION_NBT_COMPOUND_ENTRY_TO_INT =\n            FUNCTION_NBT_COMPOUND_ENTRY.appendPost(PROPAGATOR_INTEGER_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Long> FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG =\n            FUNCTION_NBT_COMPOUND_ENTRY.appendPost(PROPAGATOR_LONG_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Double> FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE =\n            FUNCTION_NBT_COMPOUND_ENTRY.appendPost(PROPAGATOR_DOUBLE_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Boolean> FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN =\n            FUNCTION_NBT_COMPOUND_ENTRY.appendPost(PROPAGATOR_BOOLEAN_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, String> FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING =\n            FUNCTION_NBT_COMPOUND_ENTRY.appendPost(PROPAGATOR_STRING_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Optional<Tag>> FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT =\n            FUNCTION_NBT_COMPOUND_ENTRY.appendPost(PROPAGATOR_NBT_VALUE);\n\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Integer> FUNCTION_NBT_TO_INT =\n            FUNCTION_NBT.appendPost(PROPAGATOR_INTEGER_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Long> FUNCTION_NBT_TO_LONG =\n            FUNCTION_NBT.appendPost(PROPAGATOR_LONG_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Double> FUNCTION_NBT_TO_DOUBLE =\n            FUNCTION_NBT.appendPost(PROPAGATOR_DOUBLE_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Boolean> FUNCTION_NBT_TO_BOOLEAN =\n            FUNCTION_NBT.appendPost(PROPAGATOR_BOOLEAN_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, String> FUNCTION_NBT_TO_STRING =\n            FUNCTION_NBT.appendPost(PROPAGATOR_STRING_VALUE);\n    public static final IterativeFunction.PrePostBuilder<Optional<Tag>, Optional<Tag>> FUNCTION_NBT_TO_NBT =\n            FUNCTION_NBT.appendPost(PROPAGATOR_NBT_VALUE);\n\n    public static final IterativeFunction.PrePostBuilder<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>\n            FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT = FUNCTION_NBT_COPY_FOR_VALUE.appendPost(PROPAGATOR_NBT_COMPOUND_VALUE);\n\n    // --------------- Ingredients builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INGREDIENTS = OperatorBuilder.forType(ValueTypes.OBJECT_INGREDIENTS).appendKind(\"ingredients\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INGREDIENTS_1_PREFIX_LONG = INGREDIENTS\n            .inputTypes(ValueTypes.OBJECT_INGREDIENTS).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INGREDIENTS_3_ITEMSTACK = INGREDIENTS\n            .inputTypes(ValueTypes.OBJECT_INGREDIENTS, ValueTypes.INTEGER, ValueTypes.OBJECT_ITEMSTACK)\n            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).output(ValueTypes.OBJECT_INGREDIENTS);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INGREDIENTS_3_FLUIDSTACK = INGREDIENTS\n            .inputTypes(ValueTypes.OBJECT_INGREDIENTS, ValueTypes.INTEGER, ValueTypes.OBJECT_FLUIDSTACK)\n            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).output(ValueTypes.OBJECT_INGREDIENTS);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INGREDIENTS_3_LONG = INGREDIENTS\n            .inputTypes(ValueTypes.OBJECT_INGREDIENTS, ValueTypes.INTEGER, ValueTypes.LONG)\n            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).output(ValueTypes.OBJECT_INGREDIENTS);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> INGREDIENTS_2_LIST = INGREDIENTS\n            .inputTypes(ValueTypes.OBJECT_INGREDIENTS, ValueTypes.LIST)\n            .renderPattern(IConfigRenderPattern.INFIX_LONG).output(ValueTypes.OBJECT_INGREDIENTS);\n\n    public static OperatorBase.IFunction createFunctionIngredientsList(Callable<IngredientComponent<?, ?>> componentReference) {\n        return variables -> {\n            IngredientComponent<?, ?> component = null;\n            try {\n                component = componentReference.call();\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            IIngredientComponentHandler componentHandler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n            ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n            List<?> list = Lists.newArrayList();\n            if (value.getRawValue().isPresent()) {\n                list = value.getRawValue().get().getInstances(component);\n            }\n            return ValueTypeList.ValueList.ofList(componentHandler.getValueType(), list.stream()\n                    .map(i -> componentHandler.toValue(i)).collect(Collectors.toList()));\n        };\n    }\n\n    public static <VT extends IValueType<V>, V extends IValue, T, M> List<T> unwrapIngredientComponentList(IngredientComponent<T, M> component,\n                                                                                                           ValueTypeList.ValueList<VT, V> list)\n            throws EvaluationException {\n        IIngredientComponentHandler<VT, V, T, M> componentHandler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n        if (list.getRawValue().getValueType() != componentHandler.getValueType()) {\n            throw new EvaluationException(Component.translatable(\n                    L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                    Component.translatable(componentHandler.getValueType().getTranslationKey()),\n                    Component.translatable(list.getRawValue().getValueType().getTranslationKey())));\n        }\n        List<T> listTransformed = Lists.newArrayListWithExpectedSize(list.getRawValue().getLength());\n        for (V value : list.getRawValue()) {\n            listTransformed.add(componentHandler.toInstance(value));\n        }\n        return listTransformed;\n    }\n\n    // --------------- Recipe builders ---------------\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> RECIPE = OperatorBuilder.forType(ValueTypes.OBJECT_RECIPE).appendKind(\"recipe\");\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> RECIPE_1_SUFFIX_LONG = RECIPE\n            .inputTypes(ValueTypes.OBJECT_RECIPE).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> RECIPE_2_INFIX = RECIPE\n            .inputTypes(ValueTypes.OBJECT_RECIPE, ValueTypes.OBJECT_INGREDIENTS)\n            .renderPattern(IConfigRenderPattern.INFIX_LONG);\n    public static final OperatorBuilder<OperatorBase.SafeVariablesGetter> RECIPE_2_PREFIX = RECIPE\n            .inputTypes(ValueTypes.OBJECT_INGREDIENTS, ValueTypes.OBJECT_INGREDIENTS)\n            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG);\n\n    // --------------- Capability helpers ---------------\n\n    /**\n     * Helper function to create an operator function builder for deriving capabilities from an itemstack.\n     * @param capabilityReference The capability instance reference.\n     * @param <T> The capability type.\n     * @param <C> The capability context type.\n     * @return The builder.\n     */\n    public static <T, C> IterativeFunction.PrePostBuilder<T, IValue> getItemCapability(@Nullable final ICapabilityReference<T, C> capabilityReference) {\n        return IterativeFunction.PrePostBuilder.begin()\n                .appendPre(input -> {\n                    ValueObjectTypeItemStack.ValueItemStack a = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                    if(!a.getRawValue().isEmpty()) {\n                        return a.getRawValue().getCapability(capabilityReference.getReference(), null);\n                    }\n                    return null;\n                });\n    }\n\n    public static interface ICapabilityReference<T, C> {\n        public ItemCapability<T, C> getReference();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/ProxyVariableFacadeHandler.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IProxyVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.core.item.ProxyVariableFacade;\n\n/**\n * Handler for proxy variable facades.\n * @author rubensworks\n */\npublic class ProxyVariableFacadeHandler implements IVariableFacadeHandler<IProxyVariableFacade> {\n\n    private static final IProxyVariableFacade INVALID_FACADE = new ProxyVariableFacade(false, -1);\n    private static ProxyVariableFacadeHandler _instance;\n\n    private ProxyVariableFacadeHandler() {\n\n    }\n\n    public static ProxyVariableFacadeHandler getInstance() {\n        if(_instance == null) _instance = new ProxyVariableFacadeHandler();\n        return _instance;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"proxy\");\n    }\n\n    @Override\n    public IProxyVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, int id, CompoundTag tag) {\n        if(!tag.contains(\"partId\")) {\n            return INVALID_FACADE;\n        }\n        return new ProxyVariableFacade(id, tag.getInt(\"partId\").orElseThrow());\n    }\n\n    @Override\n    public void setVariableFacade(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag, IProxyVariableFacade variableFacade) {\n        tag.putInt(\"partId\", variableFacade.getProxyId());\n    }\n\n    @Override\n    public boolean isInstance(IVariableFacade variableFacade) {\n        return variableFacade instanceof IProxyVariableFacade;\n    }\n\n    @Override\n    public boolean isInstance(IVariable<?> variable) {\n        return variable instanceof IVariable;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/build/OperatorBuilder.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.build;\n\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.IOperatorValuePropagator;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.IterativeFunction;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.OperatorBase;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Immutable builder for operators.\n * Appending the kinds \"a\", \"b\" and \"c\" for example will result in the base localization key of \"operator.operators.MOD_ID.a.b.c.\".\n * This base key then will be suffixed with {\"name\", \"basename\"}.\n * If the operator name would be \"xyz\", then you'll also need the localization keys \"operator.operators.MOD_ID.a.b.c.xyz.\"{\"name\", \"info\"}.\n *\n * The actual operator function can either be set by calling {@link OperatorBuilder#function} or by\n * doing any number of calls to {@link OperatorBuilder#handle(IOperatorValuePropagator)} with value propagators.\n * @author rubensworks\n * @param <O> The current output type for value propagators.\n */\npublic class OperatorBuilder<O> {\n\n    private final IValueType outputType;\n    private final String symbol;\n    private final String operatorName;\n    private final String interactName;\n    @Nullable\n    private final String globalInteractNamePrefix;\n    private final boolean alsoPrefixLocalScope;\n    private final IValueType[] inputTypes;\n    private final OperatorBase.IFunction function;\n    private final IConfigRenderPattern renderPattern;\n    private final String modId;\n    private final List<String> kinds;\n    private final IConditionalOutputTypeDeriver conditionalOutputTypeDeriver;\n    private final ITypeValidator typeValidator;\n    private final List<IOperatorValuePropagator> valuePropagators;\n\n    protected OperatorBuilder(String symbol, String operatorName, String interactName, String globalInteractNamePrefix, boolean alsoPrefixLocalScope,\n                              IValueType[] inputTypes, IValueType outputType,\n                              OperatorBase.IFunction function, IConfigRenderPattern renderPattern, String modId,\n                              List<String> kinds, IConditionalOutputTypeDeriver conditionalOutputTypeDeriver,\n                              ITypeValidator typeValidator, List<IOperatorValuePropagator> valuePropagators) {\n        this.symbol = symbol;\n        this.operatorName = operatorName;\n        this.interactName = interactName;\n        this.globalInteractNamePrefix = globalInteractNamePrefix;\n        this.alsoPrefixLocalScope = alsoPrefixLocalScope;\n        this.inputTypes = inputTypes;\n        this.outputType = outputType;\n        this.function = function;\n        this.renderPattern = renderPattern;\n        this.modId = modId;\n        this.kinds = kinds;\n        this.conditionalOutputTypeDeriver = conditionalOutputTypeDeriver;\n        this.typeValidator = typeValidator;\n        this.valuePropagators = valuePropagators;\n    }\n\n    /**\n     * Set the operator output value type.\n     * @param outputType The output value type.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> output(IValueType outputType) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the operator symbol.\n     * @param symbol The symbol for the operator.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> symbol(String symbol) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the operator name, used for localization.\n     * @param operatorName The operator name.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> operatorName(String operatorName) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the unique interact name.\n     * @param interactName The unique interact name.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> interactName(String interactName) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the unique interact name.\n     * @param interactName The unique interact name.\n     * @param globalInteractNamePrefix The prefix to use for the global interact name.\n     * @param alsoPrefixLocalScope If the prefix should also be used for the local scope.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> interactName(String interactName, String globalInteractNamePrefix, boolean alsoPrefixLocalScope) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the symbol and operator name to the given value.\n     * The symbol is used for display while the operator name is used for localization.\n     * @param symbolOperator The symbol and operator name.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> symbolOperator(String symbolOperator) {\n        return new OperatorBuilder<>(symbolOperator, symbolOperator, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern,\n                modId, kinds, conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the symbol, operator, and unique interact name to the given value.\n     * The symbol is used for display while the operator name is used for localization.\n     * @param symbolOperator The symbol, operator, and unique name.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> symbolOperatorInteract(String symbolOperator) {\n        return new OperatorBuilder<>(symbolOperator, symbolOperator, symbolOperator, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern,\n                modId, kinds, conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the operator, and unique interact name to the given value.\n     * @param operatorInteract The operator and interact name.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> operatorInteract(String operatorInteract) {\n        return new OperatorBuilder<>(symbol, operatorInteract, operatorInteract, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern,\n                modId, kinds, conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the input types for the operator.\n     * @param inputTypes Array of value types.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> inputTypes(IValueType... inputTypes) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set input types to a given amount of the given value type.\n     * @param length The amount of input types.\n     * @param defaultType The input type to replicate `length` times.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> inputTypes(int length, IValueType defaultType) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, OperatorBase.constructInputVariables(length, defaultType),\n                outputType, function, renderPattern, modId, kinds, conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set a single input type.\n     * @param inputType The input type.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> inputType(IValueType inputType) {\n        return inputTypes(1, inputType);\n    }\n\n    /**\n     * Set the function the operator should use.\n     * @param function The function.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> function(OperatorBase.IFunction function) {\n        if(this.valuePropagators != null) {\n            throw new IllegalStateException(\"Can not add a function when value propagators are present.\");\n        }\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the render pattern for this operator in guis.\n     * @param renderPattern The render pattern.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> renderPattern(IConfigRenderPattern renderPattern) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the mod id, by default this will be the Integrated Dynamics mod id.\n     * @param modId The mod id.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> modId(String modId) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Append a localization key element.\n     * @param kind The string to append.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> appendKind(String kind) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId,\n                Helpers.joinList(kinds, kind), conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the conditional output type deriver.\n     * @param conditionalOutputTypeDeriver The output type deriver based on certain input.\n     *                                     This will be used for {@link IOperator#getConditionalOutputType(IVariable[])}.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> conditionalOutputTypeDeriver(IConditionalOutputTypeDeriver conditionalOutputTypeDeriver) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId,\n                kinds, conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Set the type validator.\n     * @param typeValidator The type validator. This will be used for {@link IOperator#validateTypes(IValueType[])}.\n     * @return The builder instance.\n     */\n    public OperatorBuilder<O> typeValidator(ITypeValidator typeValidator) {\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId,\n                kinds, conditionalOutputTypeDeriver, typeValidator, valuePropagators);\n    }\n\n    /**\n     * Add a value propagator.\n     * @param valuePropagator The value propagator.\n     * @return The builder instance.\n     * @param <O2> The new output type for value propagators.\n     */\n    public <O2> OperatorBuilder<O2> handle(IOperatorValuePropagator<O, O2> valuePropagator) {\n        if(this.function != null) {\n            throw new IllegalStateException(\"Can not add a function when value propagators are present.\");\n        }\n        return new OperatorBuilder<>(symbol, operatorName, interactName, globalInteractNamePrefix, alsoPrefixLocalScope, inputTypes, outputType, function, renderPattern, modId, kinds,\n                conditionalOutputTypeDeriver, typeValidator, Helpers.joinList(valuePropagators, valuePropagator));\n    }\n\n    /**\n     * Build an operator from the current builder state.\n     * @return The built operator\n     */\n    public IOperator build() {\n        return new Built(this);\n    }\n\n    /**\n     * Create a new builder with the given output type.\n     * @param outputType The output type.\n     * @return The builder instance.\n     */\n    public static OperatorBuilder<OperatorBase.SafeVariablesGetter> forType(IValueType<?> outputType) {\n        return new OperatorBuilder<>(null, null, null, null, false, null, outputType, null, null, Reference.MOD_ID,\n                Collections.<String>emptyList(), null, null, null);\n    }\n\n    private static class Built extends OperatorBase {\n\n        private final String modId;\n        private final String unlocalizedType;\n        private final IConditionalOutputTypeDeriver conditionalOutputTypeDeriver;\n        private final ITypeValidator typeValidator;\n\n        protected Built(OperatorBuilder operatorBuilder) {\n            super(Objects.requireNonNull(operatorBuilder.symbol),\n                    Objects.requireNonNull(operatorBuilder.operatorName),\n                    Objects.requireNonNull(operatorBuilder.interactName),\n                    operatorBuilder.globalInteractNamePrefix,\n                    operatorBuilder.alsoPrefixLocalScope,\n                    Objects.requireNonNull(operatorBuilder.inputTypes),\n                    Objects.requireNonNull(operatorBuilder.outputType),\n                    Objects.requireNonNull(deriveFunction(operatorBuilder)), Objects.requireNonNull(operatorBuilder.renderPattern));\n            this.modId = Objects.requireNonNull(operatorBuilder.modId);\n            this.unlocalizedType = deriveUnlocalizedType(operatorBuilder);\n            this.conditionalOutputTypeDeriver = operatorBuilder.conditionalOutputTypeDeriver;\n            this.typeValidator = operatorBuilder.typeValidator;\n        }\n\n        protected static IFunction deriveFunction(OperatorBuilder operatorBuilder) {\n            if(operatorBuilder.valuePropagators != null) {\n                return new IterativeFunction(operatorBuilder.valuePropagators);\n            } else {\n                return Objects.requireNonNull(operatorBuilder.function);\n            }\n        }\n\n        protected static String deriveUnlocalizedType(OperatorBuilder<?> operatorBuilder) {\n            StringBuilder sb = new StringBuilder();\n            boolean first = true;\n            for(String kind : operatorBuilder.kinds) {\n                if(!first) {\n                    sb.append(\".\");\n                }\n                first = false;\n                sb.append(kind);\n            }\n            return sb.toString();\n        }\n\n        @Override\n        protected String getModId() {\n            return this.modId;\n        }\n\n        @Override\n        protected String getUnlocalizedType() {\n            return this.unlocalizedType;\n        }\n\n        @Override\n        public IValueType getConditionalOutputType(IVariable[] input) {\n            return conditionalOutputTypeDeriver != null\n                    ? conditionalOutputTypeDeriver.getConditionalOutputType(this, input)\n                    : super.getConditionalOutputType(input);\n        }\n\n        @Override\n        public MutableComponent validateTypes(IValueType[] input) {\n            return typeValidator != null\n                    ? typeValidator.validateTypes(this, input)\n                    : super.validateTypes(input);\n        }\n    }\n\n    public static interface IConditionalOutputTypeDeriver {\n\n        public IValueType getConditionalOutputType(OperatorBase operator, IVariable[] input);\n\n    }\n\n    public static interface ITypeValidator {\n\n        /**\n         * Check the given input value types for this operator.\n         * @param operator The operator that is being validated.\n         * @param input The ordered input value types.\n         * @return An error or null if valid.\n         */\n        public MutableComponent validateTypes(OperatorBase operator, IValueType[] input);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/expression/LazyExpression.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.expression;\n\nimport net.minecraft.network.chat.Component;\nimport org.apache.logging.log4j.Level;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.IExpression;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.ILazyExpressionValueCache;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.VariableAdapter;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * A generic expression with arbitrarily nested binary operations.\n * This is evaluated in a lazy manner.\n * @author rubensworks\n */\npublic class LazyExpression<V extends IValue> extends VariableAdapter<V> implements IExpression<V> {\n\n    private final int id;\n    private final IOperator op;\n    private final IVariable[] input;\n    private final ILazyExpressionValueCache valueCache;\n    private boolean errored = false;\n\n    public LazyExpression(int id, IOperator op, IVariable[] input, ILazyExpressionValueCache valueCache) {\n        this.id = id;\n        this.op = op;\n        this.input = input;\n        this.valueCache = valueCache;\n\n        // Make sure that any previous values become un-cached,\n        // so that the first evaluation of this expression is guaranteed to happen.\n        valueCache.removeValue(id);\n    }\n\n    @Override\n    public IValue evaluate() throws EvaluationException {\n        if(valueCache.hasValue(id)) {\n            return valueCache.getValue(id);\n        }\n        if (GeneralConfig.logCardEvaluation) {\n            IntegratedDynamics.clog(Level.INFO, \"Evaluating variable card with ID: \" + id);\n        }\n        IValue value = op.evaluate(input);\n        for (IVariable inputVariable : input) {\n            inputVariable.addInvalidationListener(this);\n        }\n        valueCache.setValue(id, value);\n        return value;\n    }\n\n    @Override\n    public boolean hasErrored() {\n        return errored;\n    }\n\n    @Override\n    public IValueType<V> getType() {\n        return op.getConditionalOutputType(input);\n    }\n\n    @Override\n    public V getValue() throws EvaluationException {\n        IValue value;\n        try {\n            value = evaluate();\n        } catch (EvaluationException e) {\n            errored = true;\n            throw e;\n        }\n        try {\n            return (V) value;\n        } catch (ClassCastException e) {\n            errored = true;\n            EvaluationException e2 = new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPEOUTPUT,\n                    op,\n                    Component.translatable(value.getType().getTranslationKey()),\n                    Component.translatable(op.getOutputType().getTranslationKey())));\n            e2.addResolutionListeners(this::invalidate);\n            throw e2;\n        }\n    }\n\n    @Override\n    public void invalidate() {\n        valueCache.removeValue(id);\n        super.invalidate();\n        for (IVariable inputVariable : input) {\n            inputVariable.removeInvalidationListener(this);\n        }\n    }\n\n    public IOperator getOperator() {\n        return op;\n    }\n\n    public IVariable[] getInput() {\n        return input;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/CastOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueCastRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.function.Consumer;\n\n/**\n * Base class for cast operators.\n * @author rubensworks\n */\npublic class CastOperator<T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> extends OperatorBase {\n\n    private final T1 from;\n    private final T2 to;\n    private final IValueCastRegistry.IMapping<T1, T2, V1, V2> mapping;\n\n    public CastOperator(final T1 from, final T2 to, final IValueCastRegistry.IMapping<T1, T2, V1, V2> mapping) {\n        super(\"()\", from.getTranslationKey() + \"$\" + to.getTranslationKey(), from.getTypeName() + \"To\" + Helpers.capitalizeString(to.getTypeName()), null, false, constructInputVariables(1, from), to, new IFunction() {\n            @Override\n            public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n                IValue value = variables.getValue(0);\n                if(value.getType() != from) {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.OPERATOR_ERROR_CAST_UNEXPECTED, Component.translatable(value.getType().getTranslationKey()), Component.translatable(from.getTranslationKey()), Component.translatable(to.getTranslationKey())));\n                }\n                return mapping.cast((V1) value);\n            }\n        }, IConfigRenderPattern.PREFIX_1);\n        this.from = from;\n        this.to = to;\n        this.mapping = mapping;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"operator.\" + getModId() + \".cast\"\n                + from.getUniqueName().toString().replaceAll(\":\", \"_\") + \"__\"\n                + to.getUniqueName().toString().replaceAll(\":\", \"_\"));\n    }\n\n    @Override\n    public String getUnlocalizedType() {\n        return \"cast\";\n    }\n\n    @Override\n    protected String getUnlocalizedPrefix() {\n        return \"operator.\" + getModId() + \".\" + getUnlocalizedType();\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo) {\n        tooltipAdder.accept(Component.translatable(\"operator.integrateddynamics.cast.tooltip\",\n                Component.translatable(from.getTranslationKey()),\n                Component.translatable(to.getTranslationKey())));\n        super.loadTooltip(tooltipAdder, appendOptionalInfo);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/CombinedOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorSerializer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.Variable;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * An operator that somehow combines one or more operators.\n * @author rubensworks\n */\npublic class CombinedOperator extends OperatorBase {\n\n    private final String unlocalizedType;\n\n    public CombinedOperator(String symbol, String operatorName, String interactName, OperatorsFunction function, IValueType outputType) {\n        this(symbol, operatorName, interactName, function, new IValueType[]{ValueTypes.CATEGORY_ANY}, outputType, null);\n    }\n\n    public CombinedOperator(String symbol, String operatorName, String interactName, OperatorsFunction function, IValueType[] inputTypes,\n                            IValueType outputType, @Nullable IConfigRenderPattern configRenderPattern) {\n        super(symbol, operatorName, interactName, null, false,\n                inputTypes, outputType, function, configRenderPattern);\n        this.unlocalizedType = \"virtual\";\n    }\n\n    @Override\n    protected String getUnlocalizedType() {\n        return unlocalizedType;\n    }\n\n    @Override\n    public IOperator materialize() {\n        return this;\n    }\n\n    public static abstract class OperatorsFunction implements IFunction {\n\n        private final IOperator[] operators;\n\n        public OperatorsFunction(IOperator... operators) {\n            this.operators = operators;\n        }\n\n        public IOperator[] getOperators() {\n            return operators;\n        }\n\n        public int getInputOperatorCount() {\n            return getOperators().length;\n        }\n    }\n\n    public static class Conjunction extends OperatorsFunction {\n\n        public Conjunction(IOperator... operators) {\n            super(operators);\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            IValue value = variables.getValue(0);\n            for (IOperator operator : getOperators()) {\n                IValue result = ValueHelpers.evaluateOperator(operator, value);\n                ValueHelpers.validatePredicateOutput(operator, result);\n                if (!((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {\n                    return ValueTypeBoolean.ValueBoolean.of(false);\n                }\n            }\n            return ValueTypeBoolean.ValueBoolean.of(true);\n        }\n\n        public static CombinedOperator asOperator(IOperator... operators) {\n            CombinedOperator.Conjunction conjunction = new CombinedOperator.Conjunction(operators);\n            return new CombinedOperator(\":&&:\", \"p_conjunction\", \"p_conjunction\", conjunction, ValueTypes.BOOLEAN);\n        }\n\n        public static class Serializer extends ListOperatorSerializer<Conjunction> {\n\n            public Serializer() {\n                super(\"conjunction\", Conjunction.class);\n            }\n\n            @Override\n            public CombinedOperator newFunction(IOperator... operators) {\n                return Conjunction.asOperator(operators);\n            }\n\n        }\n    }\n\n    public static class Disjunction extends OperatorsFunction {\n\n        public Disjunction(IOperator... operators) {\n            super(operators);\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            IValue value = variables.getValue(0);\n            for (IOperator operator : getOperators()) {\n                IValue result = ValueHelpers.evaluateOperator(operator, value);\n                ValueHelpers.validatePredicateOutput(operator, result);\n                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {\n                    return ValueTypeBoolean.ValueBoolean.of(true);\n                }\n            }\n            return ValueTypeBoolean.ValueBoolean.of(false);\n        }\n\n        public static CombinedOperator asOperator(IOperator... operators) {\n            CombinedOperator.Disjunction disjunction = new CombinedOperator.Disjunction(operators);\n            return new CombinedOperator(\":||:\", \"p_disjunction\", \"p_disjunction\", disjunction, ValueTypes.BOOLEAN);\n        }\n\n        public static class Serializer extends ListOperatorSerializer<Disjunction> {\n\n            public Serializer() {\n                super(\"disjunction\", Disjunction.class);\n            }\n\n            @Override\n            public CombinedOperator newFunction(IOperator... operators) {\n                return Disjunction.asOperator(operators);\n            }\n\n        }\n    }\n\n    public static class Negation extends OperatorsFunction {\n\n        public Negation(IOperator operator) {\n            super(new IOperator[]{operator});\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            IValue value = variables.getValue(0);\n            IOperator operator = getOperators()[0];\n            IValue result = ValueHelpers.evaluateOperator(operator, value);\n            ValueHelpers.validatePredicateOutput(operator, result);\n            return ValueTypeBoolean.ValueBoolean.of(!((ValueTypeBoolean.ValueBoolean) result).getRawValue());\n        }\n\n        public static CombinedOperator asOperator(IOperator operator) {\n            CombinedOperator.Negation negation = new CombinedOperator.Negation(operator);\n            return new CombinedOperator(\"!:\", \"p_negation\", \"p_negation\", negation, ValueTypes.BOOLEAN);\n        }\n\n        public static class Serializer extends ListOperatorSerializer<Negation> {\n\n            public Serializer() {\n                super(\"negation\", Negation.class);\n            }\n\n            @Override\n            public CombinedOperator newFunction(IOperator... operators) {\n                return Negation.asOperator(operators[0]);\n            }\n\n        }\n    }\n\n    public static class Pipe extends OperatorsFunction {\n\n        public Pipe(IOperator... operators) {\n            super(operators);\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            return pipeVariablesToOperators(variables.getVariables(), getOperators());\n        }\n\n        /**\n         * Pass the first variable to all n-1 first operators.\n         * Prepend the results of these operators to the variables array.\n         * Pass the final variables array to the last operator, and return the result.\n         * @param allVariables The input variables.\n         * @param operators The operators to apply to. The n-1 first ones are the inputs, and the last one is the target to pipe to.\n         * @return The final result.\n         * @throws EvaluationException If evaluation failed.\n         */\n        public static IValue pipeVariablesToOperators(IVariable[] allVariables, IOperator[] operators) throws EvaluationException {\n            int firstInputRange = operators.length - 1;\n            IVariable input = allVariables[0];\n            IVariable[] intermediates = new IVariable[firstInputRange];\n            for (int i = 0; i < firstInputRange; ++i) {\n                intermediates[i] = new Variable<>(ValueHelpers.evaluateOperator(operators[i], input));\n            }\n            IVariable[] remaining = ArrayUtils.subarray(allVariables, 1, allVariables.length);\n            IVariable[] newVariables = ArrayUtils.addAll(intermediates, remaining);\n            return ValueHelpers.evaluateOperator(operators[operators.length - 1], newVariables);\n        }\n\n        /**\n         * Determine the input types and output type for the given operators.\n         * @param operators The operators to apply to. The n-1 first ones are the inputs, and the last one is the target to pipe to.\n         * @return The input types and output type.\n         */\n        public static Pair<IValueType[], IValueType> getPipedInputOutputTypes(IOperator[] operators) {\n            int firstInputRange = operators.length - 1;\n            IValueType[] inputTypes = new IValueType[1];\n            for (int i = 0; i < operators.length; ++i) {\n                IValueType[] operatorInputTypes = operators[i].getInputTypes();\n                if (i < firstInputRange) {\n                    if (inputTypes[0] == null) {\n                        inputTypes[0] = operatorInputTypes[0];\n                    } else {\n                        if (inputTypes[0] != operatorInputTypes[0]) {\n                            if (ValueHelpers.correspondsTo(inputTypes[0], operatorInputTypes[0])) {\n                                if (inputTypes[0].isCategory()) {\n                                    inputTypes[0] = operatorInputTypes[0];\n                                }\n                            }\n                        }\n                    }\n                } else {\n                    inputTypes = ArrayUtils.addAll(inputTypes,\n                            ArrayUtils.subarray(operatorInputTypes, firstInputRange, operatorInputTypes.length));\n                }\n            }\n            IValueType lastOutputType = operators[operators.length - 1].getOutputType();\n            if (lastOutputType == ValueTypes.OPERATOR) {\n                // If output type is an operator, make it ANY, as we don't know yet what we will pipe with.\n                lastOutputType = ValueTypes.CATEGORY_ANY;\n            }\n            return Pair.of(inputTypes, lastOutputType);\n\n        }\n\n        public static CombinedOperator asOperator(final IOperator... operators) {\n            return asOperator(new CombinedOperator.Pipe(operators), \":.:\", \"piped\", \"piped\", operators);\n        }\n\n        public static CombinedOperator asOperator(OperatorsFunction function, String symbol, String operatorName, String interactName, final IOperator... operators) {\n            Pair<IValueType[], IValueType> ioTypes = getPipedInputOutputTypes(operators);\n            return new CombinedOperator(symbol, operatorName, interactName, function, ioTypes.getRight()) {\n                @Override\n                public IValueType getConditionalOutputType(IVariable[] allVariables) {\n                    try {\n                        return pipeVariablesToOperators(allVariables, operators).getType();\n                    } catch (EvaluationException e) {\n                        return ValueTypes.CATEGORY_ANY;\n                    }\n                }\n\n                @Override\n                public IValueType[] getInputTypes() {\n                    return ioTypes.getLeft();\n                }\n            };\n        }\n\n        public static class Serializer extends ListOperatorSerializer<Pipe> {\n\n            public Serializer() {\n                super(\"pipe\", Pipe.class);\n            }\n\n            @Override\n            public CombinedOperator newFunction(IOperator... operators) {\n                return Pipe.asOperator(operators);\n            }\n\n        }\n    }\n\n    public static class Pipe2 extends OperatorsFunction {\n\n        public Pipe2(IOperator... operators) {\n            super(operators);\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            return Pipe.pipeVariablesToOperators(variables.getVariables(), getOperators());\n        }\n\n        public static CombinedOperator asOperator(IOperator... operators) {\n            return Pipe.asOperator(new CombinedOperator.Pipe2(operators), \":.2:\", \"piped2\", \"piped2\", operators);\n        }\n\n        public static class Serializer extends ListOperatorSerializer<Pipe2> {\n\n            public Serializer() {\n                super(\"pipe2\", Pipe2.class);\n            }\n\n            @Override\n            public CombinedOperator newFunction(IOperator... operators) {\n                return Pipe2.asOperator(operators);\n            }\n\n        }\n    }\n\n    public static class Flip extends OperatorsFunction {\n\n        public Flip(IOperator operator) {\n            super(new IOperator[]{operator});\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            int size = variables.getVariables().length;\n            IValue[] values = new IValue[size];\n            for (int i = 0; i < size; i++) {\n                int targetI = i < 2 ? 1 - i : i;\n                values[i] = variables.getValue(targetI);\n            }\n            return ValueHelpers.evaluateOperator(getOperators()[0], values);\n        }\n\n        public static CombinedOperator asOperator(IOperator operator) throws EvaluationException {\n            CombinedOperator.Flip flip = new CombinedOperator.Flip(operator);\n            IValueType[] originalInputTypes = operator.getInputTypes();\n            IValueType[] flippedInputTypes = new IValueType[originalInputTypes.length];\n            if (originalInputTypes.length < 2) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTHVIRTIUAL,\n                        Component.translatable(Operators.OPERATOR_FLIP.getTranslationKey()),\n                        Component.translatable(operator.getTranslationKey()),\n                        originalInputTypes.length, 2));\n            }\n            for (int i = 0; i < flippedInputTypes.length; i++) {\n                int targetI = i < 2 ? 1 - i : i;\n                flippedInputTypes[i] = originalInputTypes[targetI];\n            }\n            CombinedOperator combinedOperator;\n            try {\n                combinedOperator = new CombinedOperator(\":flip:\", \"flipped\", \"flipped\", flip, flippedInputTypes,\n                        operator.getOutputType(), null);\n            } catch (IllegalArgumentException e) {\n                throw new EvaluationException(Component.translatable(e.getMessage()));\n            }\n            return combinedOperator;\n        }\n\n        public static class Serializer extends ListOperatorSerializer<Flip> {\n\n            public Serializer() {\n                super(\"flip\", Flip.class);\n            }\n\n            @Override\n            public CombinedOperator newFunction(IOperator... operators) throws EvaluationException {\n                return Flip.asOperator(operators[0]);\n            }\n\n        }\n    }\n\n    public static abstract class ListOperatorSerializer<F extends IFunction> implements IOperatorSerializer<CombinedOperator> {\n\n        private final String functionName;\n        private final Class<F> functionClass;\n\n        public ListOperatorSerializer(String functionName, Class<F> functionClass) {\n            this.functionName = functionName;\n            this.functionClass = functionClass;\n        }\n\n        @Override\n        public boolean canHandle(IOperator operator) {\n            return operator instanceof CombinedOperator && functionClass.isInstance(((CombinedOperator) operator).getFunction());\n        }\n\n        @Override\n        public Identifier getUniqueName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"combined.\" + functionName);\n        }\n\n        @Override\n        public void serialize(ValueOutput valueOutput, CombinedOperator operator) {\n            OperatorsFunction function = (OperatorsFunction) operator.getFunction();\n            IOperator[] operators = function.getOperators();\n            ValueOutput.ValueOutputList list = valueOutput.childrenList(\"operators\");\n            for (IOperator functionOperator : operators) {\n                Operators.REGISTRY.serialize(list.addChild(), functionOperator);\n            }\n        }\n\n        @Override\n        public CombinedOperator deserialize(ValueInput valueInput) throws EvaluationException {\n            List<IOperator> operators = Lists.newArrayList();\n            for (ValueInput input : valueInput.childrenList(\"operators\").orElseThrow()) {\n                operators.add(Objects.requireNonNull(Operators.REGISTRY.deserialize(input)));\n            }\n            return newFunction(operators.toArray(new IOperator[0]));\n        }\n\n        public abstract CombinedOperator newFunction(IOperator... operators) throws EvaluationException;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/CompositionalOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.VariableAdapter;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * An operator composed of a number of other operators.\n * @author rubensworks\n */\npublic class CompositionalOperator extends OperatorBase {\n\n    private final String unlocalizedType;\n\n    protected CompositionalOperator(String symbol, String operatorName, String interactName, IValueType[] inputTypes, IValueType outputType,\n                                    IFunction function, IConfigRenderPattern renderPattern, String unlocalizedType) {\n        super(symbol, operatorName, interactName, null, false, inputTypes, outputType, function, renderPattern);\n        this.unlocalizedType = unlocalizedType;\n    }\n\n    @Override\n    protected String getUnlocalizedType() {\n        return unlocalizedType;\n    }\n\n    public static class AppliedOperatorBuilder {\n\n        private final IOperator base;\n        private int buildersRequiredInputLength = -1;\n        private AppliedOperatorBuilder[] builders = null;\n\n        public AppliedOperatorBuilder(IOperator base) {\n            this.base = base;\n        }\n\n        public AppliedOperatorBuilder apply(IOperator... operators) {\n            AppliedOperatorBuilder[] builders = new AppliedOperatorBuilder[operators.length];\n            for(int i = 0; i < operators.length; i++) {\n                builders[i] = new AppliedOperatorBuilder(operators[i]);\n            }\n            return apply(builders);\n        }\n\n        public AppliedOperatorBuilder apply(AppliedOperatorBuilder... builders) {\n            IValueType[] valueTypes = new IValueType[builders.length];\n            for(int i = 0 ; i < builders.length; i++) {\n                IOperator operator = builders[i].base;\n                valueTypes[i] = operator.getOutputType();\n            }\n            Component error = this.base.validateTypes(valueTypes);\n            if(error != null) {\n                throw new IllegalArgumentException(error.getString());\n            }\n            this.builders = builders;\n            return this;\n        }\n\n        protected int getRequiredInputLength() {\n            if(buildersRequiredInputLength == -1) {\n                if(this.builders != null) {\n                    this.buildersRequiredInputLength = Integer.MAX_VALUE;\n                    for(AppliedOperatorBuilder builder : builders) {\n                        this.buildersRequiredInputLength = Math.min(this.buildersRequiredInputLength, builder.getRequiredInputLength());\n                    }\n                } else {\n                    this.buildersRequiredInputLength = this.base.getRequiredInputLength();\n                }\n            }\n            return buildersRequiredInputLength;\n        }\n\n        protected IValueType[] getInputTypes() {\n            IValueType[] inputTypes;\n            if(this.builders == null) {\n                inputTypes = this.base.getInputTypes();\n            } else {\n                inputTypes = new IValueType[getRequiredInputLength()];\n                for(AppliedOperatorBuilder builder : builders) {\n                    IValueType[] subInputTypes = builder.getInputTypes();\n                    for(int i = 0; i < subInputTypes.length; i++) {\n                        if(inputTypes[i] != null && !ValueHelpers.correspondsTo(inputTypes[i], subInputTypes[i])) {\n                            throw new IllegalArgumentException(String.format(\"An composed operator expected type %s \" +\n                                    \"at position %s, while a type of %s was found.\", inputTypes[i], i, subInputTypes[i]));\n                        } else if(inputTypes[i] == null) {\n                            inputTypes[i] = subInputTypes[i];\n                        }\n                    }\n                }\n            }\n            return inputTypes;\n        }\n\n        protected IValue evaluate(final IVariable... variables) throws EvaluationException {\n            if(this.builders == null) {\n                return this.base.evaluate(variables);\n            } else {\n                IVariable[] subVariablesOut = new IVariable[builders.length];\n                for(int i = 0; i < builders.length; i++) {\n                    final AppliedOperatorBuilder builder = builders[i];\n                    // Anonymous class because we want lazy evaluation\n                    subVariablesOut[i] = new VariableAdapter<IValue>() {\n                        @Override\n                        public IValueType getType() {\n                            return builder.base.getOutputType();\n                        }\n\n                        @Override\n                        public IValue getValue() throws EvaluationException {\n                            return builder.evaluate(variables);\n                        }\n                    };\n                }\n                return this.base.evaluate(subVariablesOut);\n            }\n        }\n\n        protected MutableComponent validateTypes(String unlocalizedOperatorName, IValueType[] input) {\n            if(this.builders == null) {\n                return this.base.validateTypes(input);\n            } else {\n                if(input.length != getRequiredInputLength()) {\n                    return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,\n                            Component.translatable(unlocalizedOperatorName),\n                            input.length, getRequiredInputLength());\n                }\n                IValueType[] subValueTypesOut = new IValueType[builders.length];\n                for(int i = 0; i < builders.length; i++) {\n                    AppliedOperatorBuilder builder = builders[i];\n                    MutableComponent subError;\n                    if((subError = builder.validateTypes(unlocalizedOperatorName, input)) != null) {\n                        return subError;\n                    }\n                    subValueTypesOut[i] = builder.base.getOutputType();\n                }\n                return this.base.validateTypes(subValueTypesOut);\n            }\n        }\n\n        /**\n         * Make a new operator based on the applied elements.\n         * @param symbol The symbol for the operator.\n         * @param operatorName The operator name.\n         * @param interactName The unique interact name.\n         * @param renderPattern The config render pattern.\n         * @param unlocalizedType The unlocalized type name.\n         * @return The built operator.\n         */\n        public IOperator build(String symbol, String operatorName, String interactName, IConfigRenderPattern renderPattern, String unlocalizedType) {\n            return new CompositionalOperator(symbol, operatorName, interactName, getInputTypes(), this.base.getOutputType(), new IFunction() {\n                @Override\n                public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n                    return AppliedOperatorBuilder.this.evaluate(variables.getVariables());\n                }\n            }, renderPattern, unlocalizedType) {\n                @Override\n                public MutableComponent validateTypes(IValueType[] input) {\n                    return AppliedOperatorBuilder.this.validateTypes(getTranslationKey(), input);\n                }\n            };\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/CurriedOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorSerializer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.Variable;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * An operator that is partially being applied.\n * @author rubensworks\n */\npublic class CurriedOperator implements IOperator {\n\n    private final IOperator baseOperator;\n    private final IVariable[] appliedVariables;\n\n    public CurriedOperator(IOperator baseOperator, IVariable... appliedVariables) {\n        this.baseOperator = baseOperator;\n        this.appliedVariables = appliedVariables;\n    }\n\n    protected String getAppliedSymbol() {\n        String symbol = \"\";\n        for (IVariable appliedVariable : appliedVariables) {\n            symbol += appliedVariable.getType().getTypeName() + \";\";\n        }\n        return symbol;\n    }\n\n    @Override\n    public String getSymbol() {\n        StringBuilder sb = new StringBuilder();\n        sb.append(baseOperator.getSymbol());\n        sb.append(\" [\");\n        sb.append(getAppliedSymbol());\n        sb.append(\"]\");\n        return sb.toString();\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.parse(\"curried_operator\");\n    }\n\n    @Override\n    public String getInteractName() {\n        return \"curried_operator\";\n    }\n\n    @Nullable\n    @Override\n    public String getGlobalInteractNamePrefix() {\n        return null;\n    }\n\n    @Override\n    public boolean shouldAlsoPrefixLocalScope() {\n        return false;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return baseOperator.getTranslationKey();\n    }\n\n    @Override\n    public String getUnlocalizedCategoryName() {\n        return baseOperator.getUnlocalizedCategoryName();\n    }\n\n    @Override\n    public MutableComponent getLocalizedNameFull() {\n        return Component.translatable(L10NValues.OPERATOR_APPLIED_OPERATORNAME,\n                baseOperator.getLocalizedNameFull(), getAppliedSymbol());\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo) {\n        baseOperator.loadTooltip(tooltipAdder, appendOptionalInfo);\n        tooltipAdder.accept(Component.translatable(L10NValues.OPERATOR_APPLIED_TYPE, getAppliedSymbol()));\n    }\n\n    @Override\n    public IValueType[] getInputTypes() {\n        IValueType[] baseInputTypes = baseOperator.getInputTypes();\n        return Arrays.copyOfRange(baseInputTypes, appliedVariables.length, baseInputTypes.length);\n    }\n\n    @Override\n    public IValueType getOutputType() {\n        return baseOperator.getOutputType();\n    }\n\n    protected IVariable[] deriveFullInputVariables(IVariable[] partialInput) {\n        IVariable[] fullInput = new IVariable[Math.min(baseOperator.getRequiredInputLength(), partialInput.length + appliedVariables.length)];\n        for (int i = 0; i < appliedVariables.length; i++) {\n            fullInput[i] = appliedVariables[i];\n        }\n        System.arraycopy(partialInput, 0, fullInput, appliedVariables.length, fullInput.length - appliedVariables.length);\n        return fullInput;\n    }\n\n    protected IValueType[] deriveFullInputTypes(IValueType[] partialInput) {\n        IValueType[] fullInput = new IValueType[Math.min(baseOperator.getRequiredInputLength(), partialInput.length + appliedVariables.length)];\n        for (int i = 0; i < appliedVariables.length; i++) {\n            fullInput[i] = appliedVariables[i].getType();\n        }\n        System.arraycopy(partialInput, 0, fullInput, appliedVariables.length, fullInput.length - appliedVariables.length);\n        return fullInput;\n    }\n\n    @Override\n    public IValueType getConditionalOutputType(IVariable[] input) {\n        return baseOperator.getConditionalOutputType(deriveFullInputVariables(input));\n    }\n\n    @Override\n    public IValue evaluate(IVariable[] input) throws EvaluationException {\n        return baseOperator.evaluate(deriveFullInputVariables(input));\n    }\n\n    @Override\n    public int getRequiredInputLength() {\n        return baseOperator.getRequiredInputLength() - appliedVariables.length;\n    }\n\n    @Override\n    public MutableComponent validateTypes(IValueType[] input) {\n        return baseOperator.validateTypes(deriveFullInputTypes(input));\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.NONE;\n    }\n\n    @Override\n    public IOperator materialize() throws EvaluationException {\n        IVariable[] variables = new IVariable[appliedVariables.length];\n        for (int i = 0; i < appliedVariables.length; i++) {\n            IVariable appliedVariable = appliedVariables[i];\n            variables[i] = new Variable<>(appliedVariable.getType(), appliedVariable.getType().materialize(appliedVariable.getValue()));\n        }\n        return new CurriedOperator(baseOperator, variables);\n    }\n\n    public IOperator getBaseOperator() {\n        return baseOperator;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof CurriedOperator)) return false;\n        CurriedOperator that = (CurriedOperator) o;\n        return Objects.equals(baseOperator, that.baseOperator) &&\n                Arrays.equals(appliedVariables, that.appliedVariables);\n    }\n\n    @Override\n    public int hashCode() {\n        int result = Objects.hash(baseOperator);\n        result = 31 * result + Arrays.hashCode(appliedVariables);\n        return result;\n    }\n\n    public static class Serializer implements IOperatorSerializer<CurriedOperator> {\n\n        @Override\n        public boolean canHandle(IOperator operator) {\n            return operator instanceof CurriedOperator;\n        }\n\n        @Override\n        public Identifier getUniqueName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"curry\");\n        }\n\n        @Override\n        public void serialize(ValueOutput valueOutput, CurriedOperator operator) {\n            Operators.REGISTRY.serialize(valueOutput.child(\"baseOperator\"), operator.baseOperator);\n            ValueOutput.ValueOutputList list = valueOutput.childrenList(\"values\");\n            for (int i = 0; i < operator.appliedVariables.length; i++) {\n                IVariable appliedVariable = operator.appliedVariables[i];\n                IValue value;\n                try {\n                    value = appliedVariable.getValue();\n                } catch (EvaluationException e) {\n                    value = appliedVariable.getType().getDefault();\n                }\n                ValueOutput valueTag = list.addChild();\n                IValueType valueType = value.getType();\n                valueTag.putString(\"valueType\", valueType.getUniqueName().toString());\n                ValueHelpers.serializeRaw(valueTag.child(\"value\"), value);\n            }\n        }\n\n        @Override\n        public CurriedOperator deserialize(ValueInput valueInput) throws EvaluationException {\n            ValueInput.ValueInputList list = valueInput.childrenList(\"values\").orElseThrow();\n            List<IVariable> variables = Lists.newArrayList();\n            for (ValueInput valuetag : list) {\n                IValueType valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(valuetag.getString(\"valueType\").orElseThrow()));\n                IValue value = ValueHelpers.deserializeRaw(valuetag.child(\"value\").orElseThrow(), valueType);\n                variables.add(new Variable(valueType, value));\n            }\n            IOperator baseOperator = Objects.requireNonNull(Operators.REGISTRY.deserialize(valueInput.child(\"baseOperator\").orElseThrow()));\n            return new CurriedOperator(baseOperator, variables.toArray(new IVariable[0]));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/GeneralChoiceOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * A general choice operator.\n * @author rubensworks\n */\npublic class GeneralChoiceOperator extends GeneralOperator {\n\n    public GeneralChoiceOperator(String symbol, String operatorName, String interactName) {\n        super(symbol, operatorName, interactName, new IValueType[]{ValueTypes.BOOLEAN, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY}, ValueTypes.CATEGORY_ANY, new IFunction() {\n            @Override\n            public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n                boolean a = variables.getValue(0, ValueTypes.BOOLEAN).getRawValue();\n                return a ? variables.getValue(1) : variables.getValue(2);\n            }\n        }, new IConfigRenderPattern.Base(100, 22, new Pair[]{Pair.of(6, 2), Pair.of(60, 2) , Pair.of(80, 2)}, Pair.of(40, 2)));\n    }\n\n    @Override\n    public MutableComponent validateTypes(IValueType[] input) {\n        // Input size checking\n        int requiredInputLength = getRequiredInputLength();\n        if(input.length != requiredInputLength) {\n            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,\n                    this.getOperatorName(), input.length, requiredInputLength);\n        }\n        // Input types checking\n        IValueType temporarySecondInputType = null;\n        for(int i = 0; i < requiredInputLength; i++) {\n            IValueType inputType = input[i];\n            if(inputType == null) {\n                return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, this.getOperatorName(), Integer.toString(i));\n            }\n            if(i == 0 && !ValueHelpers.correspondsTo(getInputTypes()[i], inputType)) {\n                return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        this.getOperatorName(), Component.translatable(inputType.getTranslationKey()),\n                        Integer.toString(i), Component.translatable(getInputTypes()[i].getTranslationKey()));\n            } else if(i == 1) {\n                temporarySecondInputType = inputType;\n            } else if(i == 2) {\n                if(!ValueHelpers.correspondsTo(temporarySecondInputType, inputType)) {\n                    return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                            this.getOperatorName(), Component.translatable(inputType.getTranslationKey()),\n                            Integer.toString(i), Component.translatable(temporarySecondInputType.getTranslationKey()));\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public IValueType getConditionalOutputType(IVariable[] input) {\n        return input[1].getType();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/GeneralConstantOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * A general constant operator.\n * @author josephcsible\n */\npublic class GeneralConstantOperator extends GeneralOperator {\n\n    public GeneralConstantOperator(String symbol, String operatorName, String interactName) {\n        super(symbol, operatorName, interactName, new IValueType[]{ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY}, ValueTypes.CATEGORY_ANY,\n                variables -> variables.getValue(0), IConfigRenderPattern.PREFIX_2);\n    }\n\n    @Override\n    public MutableComponent validateTypes(IValueType[] input) {\n        // Input size checking\n        int requiredInputLength = getRequiredInputLength();\n        if(input.length != requiredInputLength) {\n            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,\n                    this.getOperatorName(), input.length, requiredInputLength);\n        }\n        // Input types checking\n        for(int i = 0; i < requiredInputLength; i++) {\n            IValueType inputType = input[i];\n            if(inputType == null) {\n                return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, this.getOperatorName(), Integer.toString(i));\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public IValueType getConditionalOutputType(IVariable[] input) {\n        return input[0].getType();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/GeneralIdentityOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * A general identity operator.\n * @author rubensworks\n */\npublic class GeneralIdentityOperator extends GeneralOperator {\n\n    public GeneralIdentityOperator(String symbol, String operatorName, String interactName) {\n        super(symbol, operatorName, interactName, new IValueType[]{ValueTypes.CATEGORY_ANY}, ValueTypes.CATEGORY_ANY, new IFunction() {\n            @Override\n            public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n                return variables.getValue(0);\n            }\n        }, IConfigRenderPattern.PREFIX_1);\n    }\n\n    @Override\n    public MutableComponent validateTypes(IValueType[] input) {\n        // Input size checking\n        int requiredInputLength = getRequiredInputLength();\n        if(input.length != requiredInputLength) {\n            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,\n                    this.getOperatorName(), input.length, requiredInputLength);\n        }\n        // Input types checking\n        for(int i = 0; i < requiredInputLength; i++) {\n            IValueType inputType = input[i];\n            if(inputType == null) {\n                return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, this.getOperatorName(), Integer.toString(i));\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public IValueType getConditionalOutputType(IVariable[] input) {\n        return input[0].getType();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/GeneralOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Base class for general operators.\n * @author rubensworks\n */\npublic class GeneralOperator extends OperatorBase {\n\n    public GeneralOperator(String symbol, String operatorName, String interactName, IFunction function) {\n        this(symbol, operatorName, interactName, 2, function, IConfigRenderPattern.INFIX);\n    }\n\n    public GeneralOperator(String symbol, String operatorName, String interactName, int inputLength, IFunction function, IConfigRenderPattern renderPattern) {\n        this(symbol, operatorName, interactName, constructInputVariables(inputLength, ValueTypes.BOOLEAN), ValueTypes.BOOLEAN, function, renderPattern);\n    }\n\n    protected GeneralOperator(String symbol, String operatorName, String interactName, IValueType[] inputTypes, IValueType outputType,\n                              IFunction function, IConfigRenderPattern renderPattern) {\n        super(symbol, operatorName, interactName, null, false, inputTypes, outputType, function, renderPattern);\n    }\n\n    @Override\n    public String getUnlocalizedType() {\n        return \"general\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/IterativeFunction.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.core.evaluate.IOperatorValuePropagator;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * A smart function that is made up of a list of value propagators.\n * @author rubensworks\n */\npublic class IterativeFunction implements OperatorBase.IFunction {\n\n    private final List<IOperatorValuePropagator<?, ?>> valuePropagators;\n\n    public IterativeFunction(List<IOperatorValuePropagator<?, ?>> valuePropagators) {\n        this.valuePropagators = valuePropagators;\n    }\n\n    @Override\n    public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n        Object output = variables;\n        for (IOperatorValuePropagator valuePropagator : valuePropagators) {\n            output = valuePropagator.getOutput(output);\n        }\n        return (IValue) output;\n    }\n\n    /**\n     * A builder for iterative smart functions.\n     * The idea is that this builder will buildReader two lists, one for the value propagators before\n     * the core propagator, and one for the value propagators after the core propagators.\n     * This is so that the pre- and post- list can be shared and only the core propagators\n     * is variable.\n     * @param <O> The current output of the pre-propagator list.\n     * @param <P> The current output of the post-propagator list.\n     */\n    public static class PrePostBuilder<O, P> {\n\n        private final List<IOperatorValuePropagator<?, ?>> preValuePropagators;\n        private final List<IOperatorValuePropagator<?, ?>> postValuePropagators;\n\n        private PrePostBuilder(List<IOperatorValuePropagator<?, ?>> preValuePropagators, List<IOperatorValuePropagator<?, ?>> postValuePropagators) {\n            this.preValuePropagators = preValuePropagators;\n            this.postValuePropagators = postValuePropagators;\n        }\n\n        /**\n         * @return The builder instance.\n         */\n        public static PrePostBuilder<OperatorBase.SafeVariablesGetter, IValue> begin() {\n            return new PrePostBuilder<>(Collections.<IOperatorValuePropagator<?, ?>>emptyList(), Collections.<IOperatorValuePropagator<?, ?>>emptyList());\n        }\n\n        /**\n         * Add a new pre-propagator\n         * @param valuePropagator The pre-propagator\n         * @param <O2> The new output of the pre-propagator list.\n         * @return The builder instance.\n         */\n        public <O2> PrePostBuilder<O2, P> appendPre(IOperatorValuePropagator<O, O2> valuePropagator) {\n            return new PrePostBuilder<>(Helpers.joinList(preValuePropagators, valuePropagator), Lists.newArrayList(postValuePropagators));\n        }\n\n        /**\n         * Add a new post-propagator\n         * @param valuePropagator The post-propagator\n         * @param <P2> The new output of the post-propagator list.\n         * @return The builder instance.\n         */\n        public <P2> PrePostBuilder<O, P2> appendPost(IOperatorValuePropagator<P2, P> valuePropagator) {\n            return new PrePostBuilder<>(Lists.newArrayList(preValuePropagators), Helpers.joinList(postValuePropagators, valuePropagator));\n        }\n\n        /**\n         * Finalize the function with the given core propagator.\n         * @param valuePropagator The core propagator that will be connected to the pre- and post-list.\n         * @return The built function.\n         */\n        public IterativeFunction build(IOperatorValuePropagator<O, P> valuePropagator) {\n            List<IOperatorValuePropagator<?, ?>> valuePropagators = Lists.newArrayListWithExpectedSize(preValuePropagators.size() + postValuePropagators.size());\n            valuePropagators.addAll(preValuePropagators);\n            valuePropagators.add(valuePropagator);\n            valuePropagators.addAll(postValuePropagators);\n            return new IterativeFunction(valuePropagators);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/OperatorBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport javax.annotation.Nullable;\nimport java.util.Arrays;\nimport java.util.function.Consumer;\n\n/**\n * A basic abstract implementation of an operator.\n * @author rubensworks\n */\npublic abstract class OperatorBase implements IOperator {\n\n    private final String symbol;\n    private final String operatorName;\n    private final String interactName;\n    @Nullable\n    private final String globalInteractNamePrefix;\n    private final boolean alsoPrefixLocalScope;\n    private final IValueType[] inputTypes;\n    private final IValueType outputType;\n    private final IFunction function;\n    @Nullable\n    private final IConfigRenderPattern renderPattern;\n\n    private String translationKey = null;\n    private int recursiveInvocations;\n\n    protected OperatorBase(String symbol, String operatorName, String interactName, String globalInteractNamePrefix, boolean alsoPrefixLocalScope, IValueType[] inputTypes,\n                           IValueType outputType, IFunction function, @Nullable IConfigRenderPattern renderPattern) {\n        this.symbol = symbol;\n        this.operatorName = operatorName;\n        this.interactName = interactName;\n        this.globalInteractNamePrefix = globalInteractNamePrefix;\n        this.alsoPrefixLocalScope = alsoPrefixLocalScope;\n        this.inputTypes = inputTypes;\n        this.outputType = outputType;\n        this.function = function;\n        this.renderPattern = renderPattern;\n        if(renderPattern != null && renderPattern.getSlotPositions().length != inputTypes.length) {\n            throw new IllegalArgumentException(String.format(\"The given config render pattern with %s slots is not \" +\n                    \"compatible with the number of input types %s for %s\",\n                    renderPattern.getSlotPositions().length, inputTypes.length, symbol));\n        }\n    }\n\n    public static IValueType[] constructInputVariables(int length, IValueType defaultType) {\n        IValueType[] values = new IValueType[length];\n        Arrays.fill(values, defaultType);\n        return values;\n    }\n\n    protected abstract String getUnlocalizedType();\n\n    protected IFunction getFunction() {\n        return this.function;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(getModId(), this.getUnlocalizedType().replaceAll(\"\\\\.\", \"_\") + \"_\" + getOperatorName());\n    }\n\n    @Override\n    public String getInteractName() {\n        return this.interactName;\n    }\n\n    @Override\n    @Nullable\n    public String getGlobalInteractNamePrefix() {\n        return globalInteractNamePrefix;\n    }\n\n    @Override\n    public boolean shouldAlsoPrefixLocalScope() {\n        return this.alsoPrefixLocalScope;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return translationKey != null ? translationKey : (translationKey = getUnlocalizedPrefix());\n    }\n\n    @Override\n    public String getUnlocalizedCategoryName() {\n        return getUnlocalizedCategoryPrefix();\n    }\n\n    @Override\n    public MutableComponent getLocalizedNameFull() {\n        return Component.translatable(getUnlocalizedCategoryPrefix() + \".basename\", Component.translatable(getTranslationKey()));\n    }\n\n    protected String getUnlocalizedPrefix() {\n        return \"operator.\" + getModId() + \".\" + getUnlocalizedType() + \".\" + getOperatorName();\n    }\n\n    protected String getUnlocalizedCategoryPrefix() {\n        return \"operator.\" + getModId() + \".\" + getUnlocalizedType();\n    }\n\n    protected String getOperatorName() {\n        return this.operatorName;\n    }\n\n    @Override\n    public String getSymbol() {\n        return symbol;\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo) {\n        Component operatorName = Component.translatable(getTranslationKey());\n        Component categoryName = Component.translatable(getUnlocalizedCategoryName());\n        String symbol = getSymbol();\n        String outputTypeName = IModHelpers.get().getL10NHelpers().localize(getOutputType().getTranslationKey());\n        tooltipAdder.accept(Component.translatable(L10NValues.OPERATOR_TOOLTIP_OPERATORNAME, operatorName, symbol));\n        tooltipAdder.accept(Component.translatable(L10NValues.OPERATOR_TOOLTIP_OPERATORCATEGORY, categoryName));\n        IValueType[] inputTypes = getInputTypes();\n        for(int i = 0; i < inputTypes.length; i++) {\n            tooltipAdder.accept(Component.translatable(L10NValues.OPERATOR_TOOLTIP_INPUTTYPENAME, i + 1)\n            .append(Component.translatable(inputTypes[i].getTranslationKey()))\n                    .withStyle(inputTypes[i].getDisplayColorFormat()));\n        }\n        tooltipAdder.accept(Component.translatable(L10NValues.OPERATOR_TOOLTIP_OUTPUTTYPENAME, getOutputType().getDisplayColorFormat() + outputTypeName));\n        if(appendOptionalInfo) {\n            // Global name\n            MutableComponent globalNameComponent = Component.translatable(L10NValues.GUI_OPERATOR_GLOBALNAME,\n                            ChatFormatting.WHITE + this.getGlobalInteractName() + \"(\")\n                    .withStyle(ChatFormatting.YELLOW);\n            for(int i = 0; i < inputTypes.length; i++) {\n                if (i > 0) {\n                    globalNameComponent = globalNameComponent.append(Component\n                            .literal(\", \")\n                            .withStyle(ChatFormatting.WHITE));\n                }\n                globalNameComponent = globalNameComponent.append(Component\n                        .translatable(inputTypes[i].getTranslationKey())\n                        .withStyle(inputTypes[i].getDisplayColorFormat()));\n            }\n            tooltipAdder.accept(globalNameComponent.append(Component\n                    .literal(\")\")\n                    .withStyle(ChatFormatting.WHITE)));\n\n            // Local name\n            if (this.getInputTypes().length > 0) {\n                String scopedTypeName = IModHelpers.get().getL10NHelpers().localize(this.getInputTypes()[0].getTranslationKey());\n                MutableComponent localNameComponent = Component.translatable(L10NValues.GUI_OPERATOR_LOCALNAME,\n                                this.getInputTypes()[0].getDisplayColorFormat() + scopedTypeName + \".\" + ChatFormatting.WHITE + this.getScopedInteractName() + \"(\")\n                        .withStyle(ChatFormatting.YELLOW);\n                for(int i = 1; i < inputTypes.length; i++) {\n                    if (i > 1) {\n                        localNameComponent = localNameComponent.append(Component\n                                .literal(\", \")\n                                .withStyle(ChatFormatting.WHITE));\n                    }\n                    localNameComponent = localNameComponent.append(Component\n                            .translatable(inputTypes[i].getTranslationKey())\n                            .withStyle(inputTypes[i].getDisplayColorFormat()));\n                }\n                tooltipAdder.accept(localNameComponent.append(Component\n                        .literal(\")\")\n                        .withStyle(ChatFormatting.WHITE)));\n            }\n            IModHelpers.get().getL10NHelpers().addOptionalInfo(tooltipAdder, getUnlocalizedPrefix(), net.minecraft.world.item.TooltipFlag.NORMAL);\n        }\n    }\n\n    @Override\n    public IValueType[] getInputTypes() {\n        return inputTypes;\n    }\n\n    @Override\n    public IValueType getOutputType() {\n        return outputType;\n    }\n\n    @Override\n    public IValueType getConditionalOutputType(IVariable[] input) {\n        return outputType;\n    }\n\n    @Override\n    public IValue evaluate(IVariable... input) throws EvaluationException {\n        if (this.recursiveInvocations++ > GeneralConfig.operatorRecursionLimit) {\n            this.recursiveInvocations = 0;\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_RECURSIONLIMIT,\n                    GeneralConfig.operatorRecursionLimit,\n                    Component.translatable(this.getTranslationKey())\n            ));\n        }\n        MutableComponent error = validateTypes(ValueHelpers.from(input));\n        if(error != null) {\n            this.recursiveInvocations--;\n            throw new EvaluationException(error);\n        }\n        IValue res = function.evaluate(new SafeVariablesGetter(input));\n        this.recursiveInvocations--;\n        return res;\n    }\n\n    @Override\n    public int getRequiredInputLength() {\n        return getInputTypes().length;\n    }\n\n    @Override\n    public MutableComponent validateTypes(IValueType[] input) {\n        // Input size checking\n        int requiredInputLength = getRequiredInputLength();\n        if(input.length != requiredInputLength) {\n            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,\n                    this.getOperatorName(), input.length, requiredInputLength);\n        }\n        // Input types checking\n        for(int i = 0; i < requiredInputLength; i++) {\n            IValueType inputType = input[i];\n            if(inputType == null) {\n                return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, this.getOperatorName(), Integer.toString(i));\n            }\n            if(!ValueHelpers.correspondsTo(getInputTypes()[i], inputType)) {\n                return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        this.getOperatorName(), Component.translatable(inputType.getTranslationKey()),\n                        Integer.toString(i + 1), Component.translatable(getInputTypes()[i].getTranslationKey()));\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public String toString() {\n        return \"[Operator: \" + getOperatorName() + \"]\";\n    }\n\n    protected String getModId() {\n        return Reference.MOD_ID;\n    }\n\n    @Override\n    @Nullable\n    public IConfigRenderPattern getRenderPattern() {\n        return renderPattern;\n    }\n\n    @Override\n    public IOperator materialize() throws EvaluationException {\n        return this;\n    }\n\n    public static class SafeVariablesGetter {\n\n        private final IVariable[] variables;\n\n        public SafeVariablesGetter(IVariable... variables) {\n            this.variables = variables;\n        }\n\n        public IValue getValue(int i) throws EvaluationException {\n            return variables[i].getValue();\n        }\n\n        public <V extends IValue> V getValue(int i, IValueType<V> valueType) throws EvaluationException {\n            return valueType.cast(getValue(i));\n        }\n\n        public IVariable[] getVariables() {\n            return this.variables;\n        }\n\n        public static class Shifted extends SafeVariablesGetter {\n\n            public Shifted(int start, IVariable... variables) {\n                super(Arrays.copyOfRange(variables, start, variables.length));\n            }\n        }\n    }\n\n    public static interface IFunction {\n\n        /**\n         * Evaluate this function for the given input.\n         * @param variables The input variables holder.\n         * @return The output value.\n         * @throws EvaluationException If an exception occurs while evaluating\n         */\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException;\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/OperatorRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.*;\nimport com.mojang.logging.LogUtils;\nimport com.mojang.serialization.Codec;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.IntTag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariablePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorSerializer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IOperatorVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.item.TagPathElement;\nimport org.cyclops.integrateddynamics.core.evaluate.expression.LazyExpression;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.item.OperatorVariableFacade;\nimport org.slf4j.Logger;\n\nimport java.util.*;\n\n/**\n * Registry for {@link IOperator}\n * @author rubensworks\n */\npublic class OperatorRegistry implements IOperatorRegistry {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    private static OperatorRegistry INSTANCE = new OperatorRegistry();\n    private static final IOperatorVariableFacade INVALID_FACADE = new OperatorVariableFacade(false, null, null);\n\n    private final List<IOperator> operators = Lists.newArrayList();\n    private final Map<String, IOperator> namedOperators = Maps.newHashMap();\n    private final Map<String, IOperator> globalInteractOperators = Maps.newHashMap();\n    private final Map<IValueType<?>, Map<String, IOperator>> scopedInteractOperators = Maps.newHashMap();\n    private final Multimap<List<IValueType>, IOperator> inputTypedOperators = HashMultimap.create();\n    private final Multimap<IValueType, IOperator> outputTypedOperators = HashMultimap.create();\n    private final Multimap<String, IOperator> categoryOperators = HashMultimap.create();\n    private final List<IOperatorSerializer> serializers = Lists.newArrayList();\n    private final Map<String, IOperatorSerializer> namedSerializers = Maps.newHashMap();\n    private final IOperatorSerializer DEFAULT_SERIALIZER = new OperatorSerializerDefault();\n\n    private OperatorRegistry() {\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).registerHandler(this);\n        }\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static OperatorRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <O extends IOperator> O register(O operator) {\n        operators.add(operator);\n        namedOperators.put(operator.getUniqueName().toString(), operator);\n        inputTypedOperators.put(ImmutableList.copyOf(operator.getInputTypes()), operator);\n        outputTypedOperators.put(operator.getOutputType(), operator);\n        categoryOperators.put(operator.getUnlocalizedCategoryName(), operator);\n\n        String globalInteractName = operator.getGlobalInteractName();\n        if (globalInteractOperators.containsKey(globalInteractName)) {\n            throw new IllegalStateException(\"Detected registration of an operator with non-unique global interact name: \" + operator.getUniqueName().toString() + \", \" + globalInteractOperators.get(globalInteractName).getUniqueName().toString());\n        }\n        globalInteractOperators.put(globalInteractName, operator);\n\n        if (operator.getInputTypes().length > 0) {\n            Map<String, IOperator> scopedIteracts = scopedInteractOperators.get(operator.getInputTypes()[0]);\n            if (scopedIteracts == null) {\n                scopedIteracts = Maps.newHashMap();\n                scopedInteractOperators.put(operator.getInputTypes()[0], scopedIteracts);\n            }\n            String scopedInteractName = operator.getScopedInteractName();\n            if (scopedIteracts.containsKey(scopedInteractName)) {\n                throw new IllegalStateException(\"Detected registration of an operator with non-unique scoped interact name: \" + operator.getUniqueName().toString() + \", \" + scopedIteracts.get(scopedInteractName).getUniqueName().toString());\n            }\n            scopedIteracts.put(scopedInteractName, operator);\n        }\n\n        return operator;\n    }\n\n    @Override\n    public Collection<IOperator> getOperators() {\n        return Collections.unmodifiableList(operators);\n    }\n\n    @Override\n    public IOperator getOperator(Identifier uniqueName) {\n        return namedOperators.get(uniqueName.toString());\n    }\n\n    @Override\n    public Collection<IOperator> getOperatorsWithInputTypes(IValueType... valueTypes) {\n        return inputTypedOperators.get(ImmutableList.copyOf(valueTypes));\n    }\n\n    @Override\n    public Collection<IOperator> getOperatorsWithOutputType(IValueType valueType) {\n        return outputTypedOperators.get(valueType);\n    }\n\n    @Override\n    public Collection<IOperator> getOperatorsInCategory(String categoryName) {\n        return categoryOperators.get(categoryName);\n    }\n\n    @Override\n    public void registerSerializer(IOperatorSerializer serializer) {\n        serializers.add(serializer);\n        namedSerializers.put(serializer.getUniqueName().toString(), serializer);\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, IOperator value) {\n        for (IOperatorSerializer serializer : serializers) {\n            if (serializer.canHandle(value)) {\n                valueOutput.putString(\"serializer\", serializer.getUniqueName().toString());\n                serializer.serialize(valueOutput.child(\"value\"), value);\n                return;\n            }\n        }\n        DEFAULT_SERIALIZER.serialize(valueOutput, value);\n    }\n\n    @Override\n    public IOperator deserialize(ValueInput valueInput) throws EvaluationException {\n        IOperatorSerializer serializer = valueInput.getString(\"serializer\")\n                .map(namedSerializers::get)\n                .orElse(DEFAULT_SERIALIZER);\n        if (serializer == null) { // Can be null if namedSerializers does not contain the serializer\n            throw new EvaluationException(\n                    Component.translatable(L10NValues.OPERATOR_ERROR_NO_DESERIALIZER, valueInput.toString()));\n        }\n        return serializer.deserialize(valueInput.child(\"value\").orElse(valueInput));\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"operator\");\n    }\n\n    @Override\n    public IOperatorVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, int id, CompoundTag tag) {\n        if(!tag.contains(\"operator\") || !(tag.contains(\"variableIds\") || tag.contains(\"variableIds\"))) {\n            return INVALID_FACADE;\n        }\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new TagPathElement(tag), LOGGER)) {\n            ValueInput input = TagValueInput.create(\n                    scopedCollector,\n                    valueDeseralizationContext.holderLookupProvider(),\n                    tag\n            );\n            IOperator operator;\n            try {\n                operator = deserialize(input.child(\"operator\").orElseThrow());\n            } catch (EvaluationException e) {\n                return INVALID_FACADE;\n            }\n            if(operator == null) {\n                return INVALID_FACADE;\n            }\n            int[] variableIds = input.list(\"variableIds\", Codec.INT).orElseThrow()\n                    .stream()\n                    .mapToInt(i -> i)\n                    .toArray();\n            return new OperatorVariableFacade(id, operator, variableIds);\n        } catch (IllegalArgumentException | NoSuchElementException e) {\n            return INVALID_FACADE;\n        }\n    }\n\n    @Override\n    public void setVariableFacade(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag, IOperatorVariableFacade variableFacade) {\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new TagPathElement(tag), LOGGER)) {\n            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n            serialize(valueOutput, variableFacade.getOperator());\n            tag.put(\"operator\", valueOutput.buildResult());\n        }\n        ListTag variableIds = new ListTag();\n        for (int variableId : variableFacade.getVariableIds()) {\n            variableIds.add(IntTag.valueOf(variableId));\n        }\n        tag.put(\"variableIds\", variableIds);\n    }\n\n    @Override\n    public boolean isInstance(IVariableFacade variableFacade) {\n        return variableFacade instanceof IOperatorVariableFacade;\n    }\n\n    @Override\n    public boolean isInstance(IVariable<?> variable) {\n        return variable instanceof IVariable;\n    }\n\n    @Override\n    public Map<String, IOperator> getGlobalInteractOperators() {\n        return globalInteractOperators;\n    }\n\n    @Override\n    public Map<IValueType<?>, Map<String, IOperator>> getScopedInteractOperators() {\n        return scopedInteractOperators;\n    }\n\n    public static class OperatorVariablePredicate extends VariablePredicate<LazyExpression> {\n\n        private final Optional<IOperator> operator;\n        private final Optional<Int2ObjectMap<VariablePredicate>> inputPredicates;\n\n        public OperatorVariablePredicate(Optional<IValueType> valueType, Optional<ValuePredicate> valuePredicate,\n                                         Optional<IOperator> operator, Optional<Map<Integer, VariablePredicate>> inputPredicates) {\n            super(LazyExpression.class, valueType, valuePredicate);\n            this.operator = operator;\n            this.inputPredicates = inputPredicates.map(Int2ObjectOpenHashMap::new);\n        }\n\n        public Optional<IOperator> getOperator() {\n            return operator;\n        }\n\n        @Override\n        protected boolean testTyped(LazyExpression variable) {\n            if (!super.testTyped(variable)\n                    || !(operator.isEmpty() || variable.getOperator() == operator.get())) {\n                return false;\n            }\n            if (inputPredicates.isPresent()) {\n                Int2ObjectMap<VariablePredicate> inputPredicatesMap = inputPredicates.get();\n                for (int i = 0; i < variable.getInput().length; i++) {\n                    IVariable inputVariable = variable.getInput()[i];\n                    VariablePredicate variablePredicate = inputPredicatesMap.get(i);\n                    if (variablePredicate != null && !variablePredicate.test(inputVariable)) {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n\n        public Optional<Map<Integer, VariablePredicate>> getInputPredicates() {\n            return inputPredicates.map(map -> map);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/OperatorSerializerDefault.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorSerializer;\n\n/**\n * The default serializer for operators.\n * @author rubensworks\n */\npublic class OperatorSerializerDefault implements IOperatorSerializer<IOperator> {\n\n    @Override\n    public boolean canHandle(IOperator operator) {\n        return true;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return null; // Only the default serializer can have name null\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, IOperator operator) {\n        valueOutput.putString(\"v\", operator.getUniqueName().toString());\n    }\n\n    @Override\n    public IOperator deserialize(ValueInput valueInput) {\n        return Operators.REGISTRY.getOperator(Identifier.parse(valueInput.getString(\"v\").orElseThrow()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/Operators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.*;\nimport com.google.re2j.Matcher;\nimport com.google.re2j.Pattern;\nimport com.google.re2j.PatternSyntaxException;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.IdentifierException;\nimport net.minecraft.core.component.DataComponentPatch;\nimport net.minecraft.core.component.DataComponentType;\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.core.component.TypedDataComponent;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.nbt.*;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.util.StringUtil;\nimport net.minecraft.world.entity.AgeableMob;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.ai.attributes.AttributeInstance;\nimport net.minecraft.world.entity.ai.attributes.Attributes;\nimport net.minecraft.world.entity.animal.Animal;\nimport net.minecraft.world.entity.decoration.ItemFrame;\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.entity.monster.Enemy;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.entity.projectile.ProjectileUtil;\nimport net.minecraft.world.entity.vehicle.minecart.AbstractMinecart;\nimport net.minecraft.world.item.BlockItem;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.ClipContext;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.Property;\nimport net.minecraft.world.level.material.Fluid;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.phys.*;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.common.IShearable;\nimport net.neoforged.neoforge.common.SoundActions;\nimport net.neoforged.neoforge.event.EventHooks;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.*;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.nbt.path.INbtPathExpression;\nimport org.cyclops.cyclopscore.nbt.path.NbtParseException;\nimport org.cyclops.cyclopscore.nbt.path.NbtPath;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.IOperatorValuePropagator;\nimport org.cyclops.integrateddynamics.core.evaluate.OperatorBuilders;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NbtHelpers;\nimport org.cyclops.integrateddynamics.core.ingredient.ExtendedIngredientsList;\nimport org.cyclops.integrateddynamics.core.ingredient.ExtendedIngredientsSingle;\nimport org.slf4j.Logger;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Collection of available operators.\n *\n * @author rubensworks\n */\npublic final class Operators {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    public static final IOperatorRegistry REGISTRY = constructRegistry();\n\n    private static IOperatorRegistry constructRegistry() {\n        // This also allows this registry to be used outside of a minecraft environment.\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IOperatorRegistry.class);\n        } else {\n            return OperatorRegistry.getInstance();\n        }\n    }\n\n    public static void load() {}\n\n    /**\n     * ----------------------------------- LOGICAL OPERATORS -----------------------------------\n     */\n\n    /**\n     * Short-circuit logical AND operator with two input booleans and one output boolean.\n     */\n    public static final IOperator LOGICAL_AND = REGISTRY.register(OperatorBuilders.LOGICAL_2.symbol(\"&&\").operatorInteract(\"and\")\n            .function(variables -> {\n                ValueTypeBoolean.ValueBoolean a = variables.getValue(0, ValueTypes.BOOLEAN);\n                if (!a.getRawValue()) {\n                    return ValueTypeBoolean.ValueBoolean.of(false);\n                } else {\n                    return variables.getValue(1, ValueTypes.BOOLEAN);\n                }\n            }).build());\n\n    /**\n     * Short-circuit logical AND operator with two input booleans and one output boolean.\n     */\n    public static final IOperator LOGICAL_OR = REGISTRY.register(OperatorBuilders.LOGICAL_2.symbol(\"||\").operatorInteract(\"or\")\n            .function(variables -> {\n                ValueTypeBoolean.ValueBoolean a = variables.getValue(0, ValueTypes.BOOLEAN);\n                if (a.getRawValue()) {\n                    return ValueTypeBoolean.ValueBoolean.of(true);\n                } else {\n                    return variables.getValue(1, ValueTypes.BOOLEAN);\n                }\n            }).build());\n\n    /**\n     * Logical NOT operator with one input booleans and one output boolean.\n     */\n    public static final IOperator LOGICAL_NOT = REGISTRY.register(OperatorBuilders.LOGICAL_1_PREFIX.symbol(\"!\").operatorInteract(\"not\")\n            .function(variables -> {\n                        ValueTypeBoolean.ValueBoolean valueBoolean = variables.getValue(0, ValueTypes.BOOLEAN);\n                        return ValueTypeBoolean.ValueBoolean.of(!valueBoolean.getRawValue());\n                    }\n            ).build());\n\n    /**\n     * Short-circuit logical NAND operator with two input booleans and one output boolean.\n     */\n    public static final IOperator LOGICAL_NAND = REGISTRY.register(\n            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LOGICAL_AND).build(\n                    \"!&&\", \"nand\", \"nand\", IConfigRenderPattern.INFIX, \"logical\"));\n\n    /**\n     * Short-circuit logical NAND operator with two input booleans and one output boolean.\n     */\n    public static final IOperator LOGICAL_NOR = REGISTRY.register(\n            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LOGICAL_OR).build(\n                    \"!||\", \"nor\", \"nor\", IConfigRenderPattern.INFIX, \"logical\"));\n\n    /**\n     * ----------------------------------- ARITHMETIC OPERATORS -----------------------------------\n     */\n\n    /**\n     * Arithmetic ADD operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_ADDITION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol(\"+\").operatorName(\"addition\").interactName(\"add\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.add(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * Arithmetic MINUS operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_SUBTRACTION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol(\"-\").operatorName(\"subtraction\").interactName(\"subtract\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.subtract(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * Arithmetic MULTIPLY operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_MULTIPLICATION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol(\"*\").operatorName(\"multiplication\").interactName(\"multiply\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.multiply(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * Arithmetic DIVIDE operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_DIVISION = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol(\"/\").operatorName(\"division\").interactName(\"divide\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.divide(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * Arithmetic MAX operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_MAXIMUM = REGISTRY.register(OperatorBuilders.ARITHMETIC_2_PREFIX.symbol(\"max\").operatorName(\"maximum\").interactName(\"max\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.max(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * Arithmetic MIN operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_MINIMUM = REGISTRY.register(OperatorBuilders.ARITHMETIC_2_PREFIX.symbol(\"min\").operatorName(\"minimum\").interactName(\"min\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.min(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * Arithmetic INCREMENT operator with one input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_INCREMENT = REGISTRY.register(OperatorBuilders.ARITHMETIC_1_SUFFIX.symbol(\"++\").operatorInteract(\"increment\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.increment(variables.getVariables()[0])\n            ).build());\n\n    /**\n     * Arithmetic DECREMENT operator with one input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_DECREMENT = REGISTRY.register(OperatorBuilders.ARITHMETIC_1_SUFFIX.symbol(\"--\").operatorInteract(\"decrement\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.decrement(variables.getVariables()[0])\n            ).build());\n\n    /**\n     * Arithmetic MODULO operator with two input numbers and one output number.\n     */\n    public static final IOperator ARITHMETIC_MODULUS = REGISTRY.register(OperatorBuilders.ARITHMETIC_2.symbol(\"%\").operatorInteract(\"modulus\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.modulus(variables.getVariables()[0], variables.getVariables()[1])\n            ).build());\n\n    /**\n     * ----------------------------------- INTEGER OPERATORS -----------------------------------\n     */\n\n     private static final ValueTypeInteger.ValueInteger ZERO = ValueTypeInteger.ValueInteger.of(0);\n\n    /**\n     * ----------------------------------- DOUBLE OPERATORS -----------------------------------\n     */\n\n    /**\n     * Integer SQRT operator with two input numbers and one output number.\n     */\n    public static final IOperator DOUBLE_SQRT = REGISTRY.register(OperatorBuilders.DOUBLE_1_PREFIX.symbolOperatorInteract(\"sqrt\")\n            .function(variables -> {\n                ValueTypeDouble.ValueDouble a = variables.getValue(0, ValueTypes.DOUBLE);\n                return ValueTypeDouble.ValueDouble.of(Math.sqrt(a.getRawValue()));\n            }).build());\n\n    /**\n     * Integer POW operator with two input numbers and one output number.\n     */\n    public static final IOperator DOUBLE_POW = REGISTRY.register(OperatorBuilders.DOUBLE_2.symbolOperatorInteract(\"pow\")\n            .function(variables -> {\n                ValueTypeDouble.ValueDouble a = variables.getValue(0, ValueTypes.DOUBLE);\n                ValueTypeDouble.ValueDouble b = variables.getValue(1, ValueTypes.DOUBLE);\n                return ValueTypeDouble.ValueDouble.of(Math.pow(a.getRawValue(), b.getRawValue()));\n            }).build());\n\n    /**\n     * ----------------------------------- RELATIONAL OPERATORS -----------------------------------\n     */\n\n    /**\n     * Relational == operator with two inputs of any type (but equal) and one output boolean.\n     */\n    public static final IOperator RELATIONAL_EQUALS = REGISTRY.register(OperatorBuilders.RELATIONAL\n            .inputTypes(2, ValueTypes.CATEGORY_ANY).renderPattern(IConfigRenderPattern.INFIX)\n            .symbol(\"==\").operatorInteract(\"equals\")\n            .function(\n                variables -> ValueTypeBoolean.ValueBoolean.of(variables.getValue(0).equals(variables.getValue(1)))\n            )\n            .typeValidator((operator, input) -> {\n                // Input size checking\n                int requiredInputLength = operator.getRequiredInputLength();\n                if(input.length != requiredInputLength) {\n                    return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGINPUTLENGTH,\n                            operator.getOperatorName(), input.length, requiredInputLength);\n                }\n                // Input types checking\n                IValueType temporarySecondInputType = null;\n                for(int i = 0; i < requiredInputLength; i++) {\n                    IValueType inputType = input[i];\n                    if (inputType instanceof IValueTypeNumber) {\n                        inputType = ValueTypes.CATEGORY_NUMBER;\n                    }\n                    if(inputType == null) {\n                        return Component.translatable(L10NValues.OPERATOR_ERROR_NULLTYPE, operator.getOperatorName(), Integer.toString(i));\n                    }\n                    if(i == 0) {\n                        temporarySecondInputType = inputType;\n                    } else if(i == 1) {\n                        if(!ValueHelpers.correspondsTo(temporarySecondInputType, inputType)) {\n                            return Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                                    operator.getOperatorName(), Component.translatable(inputType.getTranslationKey()),\n                                    Integer.toString(i), Component.translatable(temporarySecondInputType.getTranslationKey()));\n                        }\n                    }\n                }\n                return null;\n            })\n            .build());\n\n    /**\n     * Relational &gt; operator with two input integers and one output boolean.\n     */\n    public static final IOperator RELATIONAL_GT = REGISTRY.register(OperatorBuilders.RELATIONAL_2\n            .inputTypes(2, ValueTypes.CATEGORY_NUMBER).symbol(\">\").operatorName(\"gt\").interactName(\"greaterThan\")\n            .function(\n                variables -> ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NUMBER.greaterThan(variables.getVariables()[0], variables.getVariables()[1]))\n            ).build());\n\n    /**\n     * Relational &gt; operator with two input integers and one output boolean.\n     */\n    public static final IOperator RELATIONAL_LT = REGISTRY.register(OperatorBuilders.RELATIONAL_2\n            .inputTypes(2, ValueTypes.CATEGORY_NUMBER).symbol(\"<\").operatorName(\"lt\").interactName(\"lessThan\")\n            .function(\n                variables -> ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NUMBER.lessThan(variables.getVariables()[0], variables.getVariables()[1]))\n            ).build());\n\n    /**\n     * Relational != operator with two inputs of any type (but equal) and one output boolean.\n     */\n    public static final IOperator RELATIONAL_NOTEQUALS = REGISTRY.register(\n            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(RELATIONAL_EQUALS).build(\n                    \"!=\", \"notequals\", \"notEquals\", IConfigRenderPattern.INFIX, \"relational\"));\n\n    /**\n     * Relational &gt;= operator with two inputs of any type (but equal) and one output boolean.\n     */\n    public static final IOperator RELATIONAL_GE = REGISTRY.register(\n            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_OR).apply(RELATIONAL_EQUALS, RELATIONAL_GT).build(\n                    \">=\", \"ge\", \"greaterThanOrEquals\", IConfigRenderPattern.INFIX, \"relational\"));\n\n    /**\n     * Relational &lt;= operator with two inputs of any type (but equal) and one output boolean.\n     */\n    public static final IOperator RELATIONAL_LE = REGISTRY.register(\n            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_OR).apply(RELATIONAL_EQUALS, RELATIONAL_LT).build(\n                    \"<=\", \"le\", \"lessThanOrEquals\", IConfigRenderPattern.INFIX, \"relational\"));\n\n    /**\n     * ----------------------------------- BINARY OPERATORS -----------------------------------\n     */\n\n    /**\n     * Binary AND operator with two input integers and one output integers.\n     */\n    public static final IOperator BINARY_AND = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(\"&\").operatorName(\"and\").interactName(\"binaryAnd\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue() & b.getRawValue());\n            }).build());\n\n    /**\n     * Binary OR operator with two input integers and one output integers.\n     */\n    public static final IOperator BINARY_OR = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(\"|\").operatorName(\"or\").interactName(\"binaryOr\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue() | b.getRawValue());\n            }).build());\n\n    /**\n     * Binary XOR operator with two input integers and one output integers.\n     */\n    public static final IOperator BINARY_XOR = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(\"^\").operatorInteract(\"xor\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue() ^ b.getRawValue());\n            }).build());\n\n    /**\n     * Binary COMPLEMENT operator with one input integers and one output integers.\n     */\n    public static final IOperator BINARY_COMPLEMENT = REGISTRY.register(OperatorBuilders.BINARY_1_PREFIX.symbol(\"~\").operatorInteract(\"complement\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(~a.getRawValue());\n            }).build());\n\n    /**\n     * Binary &lt;&lt; operator with two input integers and one output integers.\n     */\n    public static final IOperator BINARY_LSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(\"<<\").operatorName(\"lshift\").interactName(\"leftShift\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue() << b.getRawValue());\n            }).build());\n\n    /**\n     * Binary &gt;&gt; operator with two input integers and one output integers.\n     */\n    public static final IOperator BINARY_RSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(\">>\").operatorName(\"rshift\").interactName(\"rightShift\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue() >> b.getRawValue());\n            }).build());\n\n    /**\n     * Binary &gt;&gt;&gt; operator with two input integers and one output integers.\n     */\n    public static final IOperator BINARY_RZSHIFT = REGISTRY.register(OperatorBuilders.BINARY_2.symbol(\">>>\").operatorName(\"rzshift\").interactName(\"unsignedRightShift\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger a = variables.getValue(0, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue() >>> b.getRawValue());\n            }).build());\n\n    /**\n     * ----------------------------------- STRING OPERATORS -----------------------------------\n     */\n\n    /**\n     * String length operator with one input string and one output integer.\n     */\n    public static final IOperator STRING_LENGTH = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX.symbol(\"len\").operatorInteract(\"length\")\n            .output(ValueTypes.INTEGER).function(variables -> {\n                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);\n                return ValueTypeInteger.ValueInteger.of(a.getRawValue().length());\n            }).build());\n\n    /**\n     * String concat operator with two input strings and one output string.\n     */\n    public static final IOperator STRING_CONCAT = REGISTRY.register(OperatorBuilders.STRING_2.symbol(\"+\").operatorInteract(\"concat\")\n            .function(variables -> {\n                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString b = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeString.ValueString.of(a.getRawValue() + b.getRawValue());\n            }).build());\n\n    /**\n     * String contains operator which checks whether a given (literal) string is contained in the given string.\n     */\n    public static final IOperator STRING_CONTAINS = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperatorInteract(\"contains\")\n        .output(ValueTypes.BOOLEAN).function(variables -> {\n                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().contains(search.getRawValue()));\n            }).build());\n\n    /**\n     * String match operator which checks whether a given regular expression is contained within a string.\n     */\n    public static final IOperator STRING_CONTAINS_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator(\"contains_regex\").interactName(\"containsRegex\")\n        .output(ValueTypes.BOOLEAN).function(variables -> {\n                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                try {\n                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());\n                    return ValueTypeBoolean.ValueBoolean.of(m.find());\n                } catch (PatternSyntaxException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                            pattern.getRawValue()));\n                }\n            }).build());\n\n    /**\n     * String match operator which checks whether a given regular expression matches a string.\n     */\n    public static final IOperator STRING_MATCHES_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator(\"matches_regex\").interactName(\"matchesRegex\")\n            .output(ValueTypes.BOOLEAN).function(variables -> {\n                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                try {\n                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());\n                    return ValueTypeBoolean.ValueBoolean.of(m.matches());\n                } catch (PatternSyntaxException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                            pattern.getRawValue()));\n                }\n            }).build());\n\n    /**\n     * String operator which returns the integral index of the first position where the search string appears in the given string.\n     */\n    public static final IOperator STRING_INDEX_OF = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator(\"index_of\").interactName(\"indexOf\")\n        .output(ValueTypes.INTEGER).function(variables -> {\n                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeInteger.ValueInteger.of(str.getRawValue().indexOf(search.getRawValue()));\n            }).build());\n\n    /**\n     * String operator which returns the integral index where the a substring matching the regular expression appears in the given string.\n     */\n    public static final IOperator STRING_INDEX_OF_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator(\"index_of_regex\").interactName(\"indexOfRegex\")\n        .output(ValueTypes.INTEGER).function(variables -> {\n                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                try {\n                    Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());\n                    if (m.find()) {\n                        return ValueTypeInteger.ValueInteger.of(m.start());\n                    } else {\n                        return ValueTypeInteger.ValueInteger.of(-1);\n                    }\n                } catch (PatternSyntaxException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                            pattern.getRawValue()));\n                }\n            }).build());\n\n    /**\n     * String match operator which checks whether a given string matches the beginning of the given string.\n     */\n    public static final IOperator STRING_STARTS_WITH = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator(\"starts_with\").interactName(\"startsWith\")\n        .output(ValueTypes.BOOLEAN).function(variables -> {\n                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().startsWith(search.getRawValue()));\n            }).build());\n\n    /**\n     * String match operator which checks whether a given string matches the end of the given string.\n     */\n    public static final IOperator STRING_ENDS_WITH = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator(\"ends_with\").interactName(\"endsWith\")\n        .output(ValueTypes.BOOLEAN).function(variables -> {\n                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeBoolean.ValueBoolean.of(str.getRawValue().endsWith(search.getRawValue()));\n            }).build());\n\n    /**\n     * String operator which splits on the given (literal) delimiter the input string .\n     */\n    public static final IOperator STRING_SPLIT_ON = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperator(\"split_on\").interactName(\"splitOn\")\n        .output(ValueTypes.LIST).function(variables -> {\n                ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                List<String> pieces = Arrays.asList(str.getRawValue().split(java.util.regex.Pattern.quote(search.getRawValue())));\n                List<ValueTypeString.ValueString> values = Lists.newArrayList();\n                for (String piece : pieces) {\n                    values.add(ValueTypeString.ValueString.of(piece));\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);\n            }).build());\n\n    /**\n     * String operator which splits on the given (regular expression) delimiter the input string.\n     */\n    public static final IOperator STRING_SPLIT_ON_REGEX = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator(\"split_on_regex\").interactName(\"splitOnRegex\")\n        .output(ValueTypes.LIST).function(variables -> {\n                ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n                try {\n                    List<String> pieces = Arrays.asList(Pattern.compile(pattern.getRawValue()).split(str.getRawValue()));\n                    List<ValueTypeString.ValueString> values = Lists.newArrayList();\n                    for (String piece : pieces) {\n                        values.add(ValueTypeString.ValueString.of(piece));\n                    }\n                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);\n                } catch (PatternSyntaxException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                            pattern.getRawValue()));\n                }\n            }).build());\n\n    /**\n     * String operator which takes the substring of the given string between the two integer indices.\n     */\n    public static final IOperator STRING_SUBSTRING = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract(\"substring\")\n        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)\n        .inputTypes(ValueTypes.INTEGER, ValueTypes.INTEGER, ValueTypes.STRING)\n        .output(ValueTypes.STRING)\n        .function(variables -> {\n            ValueTypeInteger.ValueInteger from = variables.getValue(0, ValueTypes.INTEGER);\n            ValueTypeInteger.ValueInteger to = variables.getValue(1, ValueTypes.INTEGER);\n            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);\n            if (from.getRawValue() > to.getRawValue()) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_TOGREATERTHANFROM));\n            }\n            if (from.getRawValue() < 0 || to.getRawValue() < 0) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_INDEXNEGATIVE));\n            }\n            int stringLength = str.getRawValue().length();\n            if (from.getRawValue() > stringLength || to.getRawValue() > stringLength) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SUBSTRING_LONGERTHANSTRING));\n            }\n            return ValueTypeString.ValueString.of(str.getRawValue().substring(from.getRawValue(), to.getRawValue()));\n        }).build());\n\n\n    /**\n     * String operator which matches against a regex and takes the group at the index of the integer given (including zero), in the input string. It is invalid for the pattern to not match.\n     */\n    public static final IOperator STRING_REGEX_GROUP = REGISTRY.register(OperatorBuilders.STRING.symbolOperator(\"regex_group\").interactName(\"regexGroup\")\n        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)\n        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)\n        .output(ValueTypes.STRING)\n        .function(variables -> {\n            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);\n            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);\n            if (group.getRawValue() < 0) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_INDEXNEGATIVE));\n            }\n            try {\n                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());\n                if (m.find()) {\n                    String result = m.group(group.getRawValue());\n                    return ValueTypeString.ValueString.of(result == null ? \"\" : result);\n                } else {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCH,\n                            str.getRawValue(), pattern.getRawValue()));\n                }\n            } catch (PatternSyntaxException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                        pattern.getRawValue()));\n            } catch (IndexOutOfBoundsException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_GROUP_NOMATCHGROUP,\n                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));\n            }\n        }).build()\n    );\n\n    /**\n     * String operator which matches against a regex the input string and returns a list containing all groups matched (including zero). An empty list is returned if the regex does not match.\n     */\n    public static final IOperator STRING_REGEX_GROUPS = REGISTRY.register(OperatorBuilders.STRING_2_LONG.symbolOperator(\"regex_groups\").interactName(\"regexGroups\")\n        .output(ValueTypes.LIST)\n        .function(variables -> {\n            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n            ValueTypeString.ValueString str = variables.getValue(1, ValueTypes.STRING);\n            try {\n                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());\n                if (m.find()) {\n                    List<ValueTypeString.ValueString> values = Lists.newArrayList();\n                    for (int i = 0; i <= m.groupCount(); i++) {\n                        String result = m.group(i);\n                        values.add(ValueTypeString.ValueString.of(result == null ? \"\" : result));\n                    }\n                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);\n                } else {\n                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING, Collections.<ValueTypeString.ValueString>emptyList());\n                }\n            } catch (PatternSyntaxException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                        pattern.getRawValue()));\n            }\n        }).build()\n    );\n\n    /**\n     * String operator which finds all matches of the regular expression in the given string and returns the given group for each match.\n     */\n    public static final IOperator STRING_REGEX_SCAN = REGISTRY.register(OperatorBuilders.STRING.symbolOperator(\"regex_scan\").interactName(\"regexScan\")\n        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)\n        .inputTypes(ValueTypes.STRING, ValueTypes.INTEGER, ValueTypes.STRING)\n        .output(ValueTypes.LIST)\n        .function(variables -> {\n            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n            ValueTypeInteger.ValueInteger group = variables.getValue(1, ValueTypes.INTEGER);\n            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);\n            if (group.getRawValue() < 0) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEXSCAN_INDEXNEGATIVE));\n            }\n            try {\n                Matcher m = Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue());\n                List<ValueTypeString.ValueString> values = Lists.newArrayList();\n                while (m.find()) {\n                    String match = m.group(group.getRawValue());\n                    if (match != null) {\n                        values.add(ValueTypeString.ValueString.of(match));\n                    }\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, values);\n            } catch (PatternSyntaxException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                        pattern.getRawValue()));\n            } catch (IndexOutOfBoundsException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEXSCAN_NOMATCHGROUP,\n                        str.getRawValue(), pattern.getRawValue(), group.getRawValue()));\n            }\n        }).build()\n    );\n\n    /**\n     * String operator which, finds all the matches of the (literal) search and replaces them with the given replacement, in the input string.\n     */\n    public static final IOperator STRING_REPLACE = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract(\"replace\")\n        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)\n        .inputTypes(3, ValueTypes.STRING)\n        .output(ValueTypes.STRING)\n        .function(variables -> {\n            ValueTypeString.ValueString search = variables.getValue(0, ValueTypes.STRING);\n            ValueTypeString.ValueString replacement = variables.getValue(1, ValueTypes.STRING);\n            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);\n            return ValueTypeString.ValueString.of(str.getRawValue().replaceAll(java.util.regex.Pattern.quote(search.getRawValue()), java.util.regex.Matcher.quoteReplacement(replacement.getRawValue())));\n        }).build()\n    );\n\n    /**\n     * String operator which, finds all the matches of the regular expression pattern and replaces them with the given replacement, in the input string.\n     */\n    public static final IOperator STRING_REPLACE_REGEX = REGISTRY.register(OperatorBuilders.STRING.symbolOperator(\"replace_regex\").interactName(\"replaceRegex\")\n        .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)\n        .inputTypes(3, ValueTypes.STRING)\n        .output(ValueTypes.STRING)\n        .function(variables -> {\n            ValueTypeString.ValueString pattern = variables.getValue(0, ValueTypes.STRING);\n            ValueTypeString.ValueString replacement = variables.getValue(1, ValueTypes.STRING);\n            ValueTypeString.ValueString str = variables.getValue(2, ValueTypes.STRING);\n            try {\n                return ValueTypeString.ValueString.of(Pattern.compile(pattern.getRawValue()).matcher(str.getRawValue()).replaceAll(replacement.getRawValue()));\n            } catch (PatternSyntaxException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REGEX_INVALID,\n                        pattern.getRawValue()));\n            } catch (IndexOutOfBoundsException e) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REPLACEREGEX_INVALIDGROUP,\n                        replacement.getRawValue(), e.getMessage()));\n            }\n        }).build()\n    );\n\n    /**\n     * String operator to join a list using a string delimiter\n     */\n    public static final IOperator STRING_JOIN = REGISTRY.register(OperatorBuilders.STRING.symbolOperatorInteract(\"join\")\n            .renderPattern(IConfigRenderPattern.PREFIX_2)\n            .inputTypes(ValueTypes.STRING, ValueTypes.LIST)\n            .output(ValueTypes.STRING)\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    // Prepare values\n                    ValueTypeString.ValueString delimiter = variables.getValue(0, ValueTypes.STRING);\n                    ValueTypeList.ValueList<?, ?> elements = variables.getValue(1, ValueTypes.LIST);\n                    if (!ValueHelpers.correspondsTo(elements.getRawValue().getValueType(), ValueTypes.STRING)) {\n                        throw new EvaluationException(Component.translatable(\n                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                                Component.translatable(elements.getRawValue().getValueType().getTranslationKey()),\n                                Component.translatable(ValueTypes.STRING.getTranslationKey())));\n                    }\n\n                    // Don't allow joining on an infinite list\n                    if (elements.getRawValue().isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                STRING_JOIN.getLocalizedNameFull()));\n                    }\n\n                    // Join in O(n), while type-checking each element, as the list may have been of ANY type.\n                    StringBuilder sb = new StringBuilder();\n                    for (IValue value : elements.getRawValue()) {\n                        if (value.getType() != ValueTypes.STRING) {\n                            throw new EvaluationException(Component.translatable(\n                                    L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                                    Component.translatable(value.getType().getTranslationKey()),\n                                    Component.translatable(ValueTypes.STRING.getTranslationKey())));\n                        }\n                        if (sb.length() > 0) {\n                            sb.append(delimiter.getRawValue());\n                        }\n                        sb.append(((ValueTypeString.ValueString) value).getRawValue());\n                    }\n\n                    return ValueTypeString.ValueString.of(sb.toString());\n                }\n            }).build()\n    );\n\n    /**\n     * Get a name value type name.\n     */\n    public static final IOperator NAMED_NAME = REGISTRY.register(OperatorBuilders.STRING_2.symbolOperatorInteract(\"name\")\n            .inputType(ValueTypes.CATEGORY_NAMED).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)\n            .function(\n                variables -> ValueTypeString.ValueString.of(ValueTypes.CATEGORY_NAMED.getName(variables.getVariables()[0]))\n            ).build());\n\n    /**\n     * Get a unique name value type name.\n     */\n    public static final IOperator UNIQUELYNAMED_UNIQUENAME = REGISTRY.register(OperatorBuilders.STRING_2.symbol(\"uname\").operatorName(\"unique_name\").interactName(\"uniqueName\")\n            .inputType(ValueTypes.CATEGORY_UNIQUELY_NAMED).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)\n            .function(\n                variables -> ValueTypeString.ValueString.of(ValueTypes.CATEGORY_UNIQUELY_NAMED.getUniqueName(variables.getVariables()[0]))\n            ).build());\n\n    /**\n     * Throw a custom error\n     */\n    public static final IOperator STRING_ERROR  = REGISTRY.register(OperatorBuilders.STRING_2.symbol(\"error\").operatorName(\"string_error\").interactName(\"stringError\")\n            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)\n            .function(\n                (variables) -> {\n                    throw new EvaluationException(Component.translatable(variables.getValue(0, ValueTypes.STRING).getRawValue()));\n                }\n            ).build());\n\n    /**\n     * ----------------------------------- NUMBER OPERATORS -----------------------------------\n     */\n\n    /**\n     * Number round operator with one input double and one output integers.\n     */\n    public static final IOperator NUMBER_ROUND = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol(\"|| ||\").operatorInteract(\"round\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.round(variables.getVariables()[0])\n            ).build());\n\n    /**\n     * Number ceil operator with one input double and one output integers.\n     */\n    public static final IOperator NUMBER_CEIL = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol(\"⌈ ⌉\").operatorInteract(\"ceil\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.ceil(variables.getVariables()[0])\n            ).build());\n\n    /**\n     * Number floor operator with one input double and one output integers.\n     */\n    public static final IOperator NUMBER_FLOOR = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER).symbol(\"⌊ ⌋\").operatorInteract(\"floor\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.floor(variables.getVariables()[0])\n            ).build());\n\n    /**\n     * Accepts a number, and returns a string roughly representing that number\n     */\n    public static final IOperator NUMBER_COMPACT = REGISTRY.register(OperatorBuilders.NUMBER_1_LONG\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.STRING).symbolOperatorInteract(\"compact\")\n            .function(\n                variables -> ValueTypes.CATEGORY_NUMBER.compact(variables.getVariables()[0])\n            ).build());\n\n    /**\n     * Cast any number to a double\n     */\n    public static final IOperator NUMBER_CAST_DOUBLE = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.DOUBLE)\n            .symbol(\"(double)\").operatorName(\"cast_double\").interactName(\"toDouble\")\n            .function(variables -> variables.getVariables()[0].getValue().cast(ValueTypes.DOUBLE)).build());\n\n    /**\n     * Cast any number to a double\n     */\n    public static final IOperator NUMBER_CAST_LONG = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.LONG)\n            .symbol(\"(long)\").operatorName(\"cast_long\").interactName(\"toLong\")\n            .function(variables -> variables.getVariables()[0].getValue().cast(ValueTypes.LONG)).build());\n\n    /**\n     * Cast any number to a double\n     */\n    public static final IOperator NUMBER_CAST_INTEGER = REGISTRY.register(OperatorBuilders.NUMBER_1_PREFIX\n            .inputType(ValueTypes.CATEGORY_NUMBER).output(ValueTypes.INTEGER)\n            .symbol(\"(integer)\").operatorName(\"cast_integer\").interactName(\"toInteger\")\n            .function(variables -> variables.getVariables()[0].getValue().cast(ValueTypes.INTEGER)).build());\n\n    /**\n     * ----------------------------------- NULLABLE OPERATORS -----------------------------------\n     */\n\n    /**\n     * Check if something is null\n     */\n    public static final IOperator NULLABLE_ISNULL = REGISTRY.register(OperatorBuilders.NULLABLE_1_PREFIX.symbol(\"o\").operatorName(\"isnull\").interactName(\"isNull\")\n            .inputType(ValueTypes.CATEGORY_ANY).output(ValueTypes.BOOLEAN).function(variables -> {\n                if(ValueHelpers.correspondsTo(variables.getVariables()[0].getType(), ValueTypes.CATEGORY_NULLABLE)) {\n                    return ValueTypeBoolean.ValueBoolean.of(ValueTypes.CATEGORY_NULLABLE.isNull(variables.getVariables()[0]));\n                }\n                return ValueTypeBoolean.ValueBoolean.of(false);\n            }).build());\n\n    /**\n     * Check if something is not null\n     */\n    public static final IOperator NULLABLE_ISNOTNULL = REGISTRY.register(new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT)\n            .apply(NULLABLE_ISNULL).build(\"∅\", \"isnotnull\", \"isNotNull\", IConfigRenderPattern.PREFIX_1, \"general\"));\n\n    /**\n     * ----------------------------------- LIST OPERATORS -----------------------------------\n     */\n\n    /**\n     * List operator with one input list and one output integer\n     */\n    public static final IOperator LIST_LENGTH = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.INTEGER).symbol(\"| |\").operatorInteract(\"length\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = valueList.getRawValue();\n                return ValueTypeInteger.ValueInteger.of(a.getLength());\n            }).build());\n\n    /**\n     * Check if a list is empty\n     */\n    public static final IOperator LIST_EMPTY = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX.output(ValueTypes.BOOLEAN).symbol(\"∅\").operatorName(\"empty\").interactName(\"isEmpty\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = valueList.getRawValue();\n                return ValueTypeBoolean.ValueBoolean.of(a.getLength() == 0);\n            }).build());\n\n    /**\n     * Check if a list is not empty\n     */\n    public static final IOperator LIST_NOT_EMPTY = REGISTRY.register(\n            new CompositionalOperator.AppliedOperatorBuilder(LOGICAL_NOT).apply(LIST_EMPTY).build(\n                    \"o\", \"notempty\", \"isNotEmpty\", IConfigRenderPattern.PREFIX_1, \"list\"));\n\n    /**\n     * List operator with one input list and one output integer\n     */\n    public static final IOperator LIST_ELEMENT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER}).output(ValueTypes.CATEGORY_ANY)\n            .renderPattern(IConfigRenderPattern.INFIX).symbolOperatorInteract(\"get\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = valueList.getRawValue();\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {\n                    return a.get(b.getRawValue());\n                } else {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, b.getRawValue(), a.getLength()));\n                }\n            }).conditionalOutputTypeDeriver((operator, input) -> {\n                try {\n                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();\n                    return a.getValueType();\n                } catch (ClassCastException | EvaluationException e) {\n                    return operator.getOutputType();\n                }\n            }).build());\n\n    /**\n     * List operator with one input list, one output integer, and one default value\n     */\n    public static final IOperator LIST_ELEMENT_DEFAULT = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.CATEGORY_ANY}).output(ValueTypes.CATEGORY_ANY)\n            .renderPattern(IConfigRenderPattern.INFIX_2_LONG).symbolOperator(\"get_or_default\").interactName(\"getOrDefault\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = valueList.getRawValue();\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                if (b.getRawValue() < a.getLength() && b.getRawValue() >= 0) {\n                    return a.get(b.getRawValue());\n                } else {\n                    if (!ValueHelpers.correspondsTo(a.getValueType(), variables.getVariables()[2].getType())) {\n                        throw new EvaluationException(Component.translatable(\n                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                                Component.translatable(a.getValueType().getTranslationKey()),\n                                Component.translatable(variables.getVariables()[2].getType().getTranslationKey())));\n                    }\n                    return variables.getValue(2);\n                }\n            }).conditionalOutputTypeDeriver(\n                (operator, input) -> input[2].getType()\n            ).build());\n\n    /**\n     * List contains operator that takes a list, a list element to look for and returns a boolean.\n     */\n    public static final IOperator LIST_CONTAINS = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})\n            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)\n            .output(ValueTypes.BOOLEAN).symbolOperatorInteract(\"contains\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                    if (list.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_CONTAINS.getLocalizedNameFull()));\n                    }\n                    IValue input = variables.getValue(1);\n                    for (IValue value : list) {\n                        if (value.equals(input)) {\n                            return ValueTypeBoolean.ValueBoolean.of(true);\n                        }\n                    }\n                    return ValueTypeBoolean.ValueBoolean.of(false);\n                }\n            }).build());\n\n    /**\n     * List contains operator that takes a list, a predicate that maps a list element to a boolean, a list element and returns a boolean.\n     */\n    public static final IOperator LIST_CONTAINS_PREDICATE = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})\n            .renderPattern(IConfigRenderPattern.INFIX)\n            .output(ValueTypes.BOOLEAN).symbolOperator(\"contains_p\").interactName(\"containsPredicate\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                    if (list.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_CONTAINS_PREDICATE.getLocalizedNameFull()));\n                    }\n                    IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));\n                    for (IValue value : list) {\n                        IValue result = ValueHelpers.evaluateOperator(operator, value);\n                        ValueHelpers.validatePredicateOutput(operator, result);\n                        if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {\n                            return ValueTypeBoolean.ValueBoolean.of(true);\n                        }\n                    }\n                    return ValueTypeBoolean.ValueBoolean.of(false);\n                }\n            }).build());\n\n    /**\n     * List operator with one input list, and element and one output integer\n     */\n    public static final IOperator LIST_COUNT = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)\n            .symbolOperatorInteract(\"count\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                    if (list.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_COUNT.getLocalizedNameFull()));\n                    }\n                    IValue value = variables.getValue(1);\n                    int count = 0;\n                    for (IValue listValue : list) {\n                        if (listValue.equals(value)) {\n                            count++;\n                        }\n                    }\n                    return ValueTypeInteger.ValueInteger.of(count);\n                }\n            }).build());\n\n    /**\n     * List operator with one input list, a predicate and one output integer\n     */\n    public static final IOperator LIST_COUNT_PREDICATE = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.INTEGER)\n            .symbolOperator(\"count_p\").interactName(\"countPredicate\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                    if (list.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_COUNT_PREDICATE.getLocalizedNameFull()));\n                    }\n                    IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));\n                    int count = 0;\n                    for (IValue listValue : list) {\n                        IValue result = ValueHelpers.evaluateOperator(operator, listValue);\n                        ValueHelpers.validatePredicateOutput(operator, result);\n                        if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {\n                            count++;\n                        }\n                    }\n                    return ValueTypeInteger.ValueInteger.of(count);\n                }\n            }).build());\n\n    /**\n     * Append an element to the given list\n     */\n    public static final IOperator LIST_APPEND = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.CATEGORY_ANY})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)\n            .symbolOperatorInteract(\"append\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = valueList.getRawValue();\n                IValue value = variables.getValue(1);\n                if (!ValueHelpers.correspondsTo(a.getValueType(), value.getType())) {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                            Component.translatable(a.getValueType().getTranslationKey()),\n                            Component.translatable(value.getType().getTranslationKey())));\n                }\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyAppend(a, value));\n            }).build());\n\n    /**\n     * Concatenate two lists\n     */\n    public static final IOperator LIST_CONCAT = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)\n            .symbolOperatorInteract(\"concat\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = valueList0.getRawValue();\n                ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);\n                IValueTypeListProxy b = valueList1.getRawValue();\n                if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                            Component.translatable(a.getValueType().getTranslationKey()),\n                            Component.translatable(b.getValueType().getTranslationKey())));\n                }\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyConcat(a, b));\n            }).build());\n\n    /**\n     * Build a list lazily using a start value and an operator that is applied to the previous element to get a next element.\n     */\n    public static final IOperator LIST_LAZYBUILT = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.CATEGORY_ANY, ValueTypes.OPERATOR})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)\n            .symbolOperator(\"lazybuilt\").interactName(\"lazyBuilt\")\n            .function(variables -> {\n                IValue a = variables.getValue(0);\n                IOperator operator = OperatorBuilders.getSafeOperator(variables.getValue(1, ValueTypes.OPERATOR), a.getType());\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyLazyBuilt<>(a, operator));\n            }).build());\n\n    /**\n     * Get the first element of the given list.\n     */\n    public static final IOperator LIST_HEAD = REGISTRY.register(OperatorBuilders.LIST_1_PREFIX\n            .inputTypes(new IValueType[]{ValueTypes.LIST}).output(ValueTypes.CATEGORY_ANY)\n            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).symbolOperatorInteract(\"head\")\n            .function(variables -> {\n                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = list.getRawValue();\n                if (a.getLength() > 0) {\n                    return a.get(0);\n                } else {\n                    throw new EvaluationException(Component.translatable(\n                            L10NValues.OPERATOR_ERROR_INDEXOUTOFBOUNDS, 0, a.getLength()));\n                }\n            }).conditionalOutputTypeDeriver((operator, input) -> {\n                try {\n                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[0].getValue()).getRawValue();\n                    return a.getValueType();\n                } catch (EvaluationException e) {\n                    return operator.getOutputType();\n                }\n            }).build());\n\n    /**\n     * Append an element to the given list.\n     */\n    public static final IOperator LIST_TAIL = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST})\n            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)\n            .symbolOperatorInteract(\"tail\")\n            .function(variables -> {\n                ValueTypeList.ValueList list = variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy a = list.getRawValue();\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyTail(a));\n            }).build());\n\n    /**\n     * Deduplicate the given list elements based on the given predicate.\n     */\n    public static final IOperator LIST_UNIQ_PREDICATE = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.OPERATOR})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)\n            .symbolOperator(\"uniq_p\").interactName(\"uniquePredicate\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                    if (list.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_UNIQ_PREDICATE.getLocalizedNameFull()));\n                    }\n                    final IOperator operator = OperatorBuilders.getSafePredictate(variables.getValue(1, ValueTypes.OPERATOR));\n                    List<IValue> values = new ArrayList<>();\n                    outerLoop:\n                    for(IValue value : list) {\n                        for(IValue existing : values) {\n                            IValue result;\n                            try {\n                                result = ValueHelpers.evaluateOperator(operator, value, existing);\n                                ValueHelpers.validatePredicateOutput(operator, result);\n                            } catch (EvaluationException e) {\n                                return Helpers.sneakyThrow(e);\n                            }\n                            if(((ValueTypeBoolean.ValueBoolean) result).getRawValue()) continue outerLoop;\n                        }\n                        values.add(value);\n                    }\n                    return ValueTypeList.ValueList.ofList(list.getValueType(), values);\n                }\n            }).build());\n\n    /**\n     * Deduplicate the given list elements.\n     */\n    public static final IOperator LIST_UNIQ = REGISTRY.register(OperatorBuilders.LIST\n            .inputType(ValueTypes.LIST)\n            .renderPattern(IConfigRenderPattern.PREFIX_1_LONG).output(ValueTypes.LIST)\n            .symbolOperator(\"uniq\").interactName(\"unique\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                    if (list.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_UNIQ.getLocalizedNameFull()));\n                    }\n                    return ValueTypeList.ValueList.ofList(list.getValueType(), new ArrayList<>(Sets.newLinkedHashSet(list)));\n                }\n            }).build());\n\n    /**\n     * Take a subset of the given list from the given index (inclusive) to the given index (exclusive).\n     */\n    public static final IOperator LIST_SLICE = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(ValueTypes.LIST, ValueTypes.INTEGER, ValueTypes.INTEGER)\n            .renderPattern(IConfigRenderPattern.PREFIX_3).output(ValueTypes.LIST)\n            .symbolOperatorInteract(\"slice\")\n            .function(variables -> {\n                ValueTypeList.ValueList valueList =variables.getValue(0, ValueTypes.LIST);\n                IValueTypeListProxy<IValueType<IValue>, IValue> list = valueList.getRawValue();\n                ValueTypeInteger.ValueInteger from = variables.getValue(1, ValueTypes.INTEGER);\n                ValueTypeInteger.ValueInteger to = variables.getValue(2, ValueTypes.INTEGER);\n                if (from.getRawValue() >= to.getRawValue()) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_TOGREATERTHANFROM));\n                }\n                if (from.getRawValue() < 0 || to.getRawValue() < 0){\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_SLICE_INDEXNEGATIVE));\n                }\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxySlice<>(list, from.getRawValue(), to.getRawValue()));\n            }).build());\n\n    public static final IOperator LIST_INTERSECTION = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(ValueTypes.LIST, ValueTypes.LIST)\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.LIST)\n            .symbol(\"∩\").operatorInteract(\"intersection\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList1 = variables.getValue(0, ValueTypes.LIST).getRawValue();\n                    IValueTypeListProxy<IValueType<IValue>, IValue> rawList2 = variables.getValue(1, ValueTypes.LIST).getRawValue();\n                    if (rawList1.isInfinite() || rawList2.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_INTERSECTION.getLocalizedNameFull()));\n                    }\n                    LinkedHashSet<IValue> result = Sets.newLinkedHashSet(rawList1);\n                    result.retainAll(Sets.newLinkedHashSet(rawList2));\n\n                    return ValueTypeList.ValueList.ofList(rawList1.getValueType(), result.stream().toList());\n                }\n            }).build());\n\n    /**\n     * Test list equality using set semantics.\n     */\n    public static final IOperator LIST_EQUALS_SET = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.BOOLEAN)\n            .symbol(\"=set=\").operatorInteract(\"equals_set\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy a = valueList0.getRawValue();\n                    if (a.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_EQUALS_SET.getLocalizedNameFull()));\n                    }\n                    ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);\n                    IValueTypeListProxy b = valueList1.getRawValue();\n                    if (b.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_EQUALS_SET.getLocalizedNameFull()));\n                    }\n                    if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {\n                        throw new EvaluationException(Component.translatable(\n                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                                Component.translatable(a.getValueType().getTranslationKey()),\n                                Component.translatable(b.getValueType().getTranslationKey())));\n                    }\n                    Set<Object> setA = Sets.newHashSet(a);\n                    Set<Object> setB = Sets.newHashSet(b);\n                    return ValueTypeBoolean.ValueBoolean.of(setA.equals(setB));\n                }\n            }).build());\n\n    /**\n     * Test list equality using multiset semantics.\n     */\n    public static final IOperator LIST_EQUALS_MULTISET = REGISTRY.register(OperatorBuilders.LIST\n            .inputTypes(new IValueType[]{ValueTypes.LIST, ValueTypes.LIST})\n            .renderPattern(IConfigRenderPattern.INFIX).output(ValueTypes.BOOLEAN)\n            .symbol(\"=multiset=\").operatorInteract(\"equals_multiset\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList0 = variables.getValue(0, ValueTypes.LIST);\n                    IValueTypeListProxy a = valueList0.getRawValue();\n                    if (a.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_EQUALS_MULTISET.getLocalizedNameFull()));\n                    }\n                    ValueTypeList.ValueList valueList1 = variables.getValue(1, ValueTypes.LIST);\n                    IValueTypeListProxy b = valueList1.getRawValue();\n                    if (b.isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                LIST_EQUALS_MULTISET.getLocalizedNameFull()));\n                    }\n                    if (!ValueHelpers.correspondsTo(a.getValueType(), b.getValueType())) {\n                        throw new EvaluationException(Component.translatable(\n                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                                Component.translatable(a.getValueType().getTranslationKey()),\n                                Component.translatable(b.getValueType().getTranslationKey())));\n                    }\n                    Multiset<Object> setA = HashMultiset.create(a);\n                    Multiset<Object> setB = HashMultiset.create(b);\n                    return ValueTypeBoolean.ValueBoolean.of(setA.equals(setB));\n                }\n            }).build());\n\n    /**\n     * ----------------------------------- BLOCK OBJECT OPERATORS -----------------------------------\n     */\n\n    /**\n     * Block isOpaque operator with one input block and one output boolean.\n     */\n    public static final IOperator OBJECT_BLOCK_OPAQUE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN).symbolOperator(\"opaque\").interactName(\"isOpaque\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent() && a.getRawValue().get().isSolidRender());\n            }).build());\n\n    /**\n     * The itemstack representation of the block\n     */\n    public static final IOperator OBJECT_BLOCK_ITEMSTACK = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK).symbolOperator(\"itemstack\").interactName(\"itemStack\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                return ValueObjectTypeItemStack.ValueItemStack.of(a.getRawValue().isPresent() ? IModHelpers.get().getBlockHelpers().getItemStackFromBlockState(a.getRawValue().get()) : ItemStack.EMPTY);\n            }).build());\n\n    /**\n     * The name of the mod owning this block\n     */\n    public static final IOperator OBJECT_BLOCK_MODNAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING).symbolOperatorInteract(\"mod\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    (OperatorBase.SafeVariablesGetter variables) -> {\n                        ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                        return a.getRawValue().isPresent() ? BuiltInRegistries.BLOCK.getKey(a.getRawValue().get().getBlock()) : Identifier.parse(\"\");\n                    },\n                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME\n            ))).build());\n\n    /**\n     * The breaksound of the block\n     */\n    public static final IOperator OBJECT_BLOCK_BREAKSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbol(\"break_sound\").operatorName(\"breaksound\").interactName(\"breakSound\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    OperatorBuilders.BLOCK_SOUND,\n                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getBreakSound().location().toString() : \"\",\n                    OperatorBuilders.PROPAGATOR_STRING_VALUE\n            ))).build());\n    /**\n     * The placesound of the block\n     */\n    public static final IOperator OBJECT_BLOCK_PLACESOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbol(\"place_sound\").operatorName(\"placesound\").interactName(\"placeSound\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    OperatorBuilders.BLOCK_SOUND,\n                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getPlaceSound().location().toString() : \"\",\n                    OperatorBuilders.PROPAGATOR_STRING_VALUE\n            ))).build());\n    /**\n     * The stepsound of the block\n     */\n    public static final IOperator OBJECT_BLOCK_STEPSOUND = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbol(\"step_sound\").operatorName(\"stepsound\").interactName(\"stepSound\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    OperatorBuilders.BLOCK_SOUND,\n                    (Optional<SoundType> sound) -> sound.isPresent() ? sound.get().getStepSound().location().toString() : \"\",\n                    OperatorBuilders.PROPAGATOR_STRING_VALUE\n            ))).build());\n\n    /**\n     * If the block is shearable\n     */\n    public static final IOperator OBJECT_BLOCK_ISSHEARABLE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)\n            .symbol(\"is_shearable\").operatorName(\"isshearable\").interactName(\"isShearable\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()\n                        && a.getRawValue().get().getBlock() instanceof IShearable\n                        && ((IShearable) a.getRawValue().get().getBlock()).isShearable(null, ItemStack.EMPTY, null, null));\n            }).build());\n\n    /**\n     * The block when this block is planted\n     */\n    public static final IOperator OBJECT_BLOCK_PLANTAGE = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbol(\"plant_age\").operatorName(\"plantage\").interactName(\"plantAge\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                int age = 0;\n                if (a.getRawValue().isPresent()) {\n                    for (Property<?> prop : a.getRawValue().get().getProperties()) {\n                        if (prop.getName().equals(\"age\") && prop.getValueClass() == Integer.class) {\n                            age = (Integer) a.getRawValue().get().getValue(prop);\n                        }\n                    }\n                }\n                return ValueTypeInteger.ValueInteger.of(age);\n            }).build());\n\n    /**\n     * Get a block by name.\n     */\n    public static final IOperator OBJECT_BLOCK_BY_NAME = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG\n            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_BLOCK)\n            .symbol(\"block_by_name\").operatorName(\"blockbyname\").interactName(\"blockByName\")\n            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION\n                    .build(input -> {\n                        Block block = BuiltInRegistries.BLOCK.getValue(input);\n                        return ValueObjectTypeBlock.ValueBlock.of(block.defaultBlockState());\n                    })).build());\n\n    /**\n     * Get the block properties as NBT compound tag.\n     */\n    public static final IOperator OBJECT_BLOCK_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG\n            .output(ValueTypes.NBT)\n            .symbol(\"block_props\").operatorName(\"blockproperties\").interactName(\"properties\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {\n                    CompoundTag tag = new CompoundTag();\n                    for (Property property : blockState.getProperties()) {\n                        Comparable<?> value = blockState.getValue(property);\n                        tag.putString(property.getName(), property.getName(value));\n                    }\n                    return tag;\n                }));\n            }).build());\n\n    /**\n     * Get the given block applied with the given properties.\n     */\n    public static final IOperator OBJECT_BLOCK_WITH_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_INFIX_VERYLONG\n            .inputTypes(ValueTypes.OBJECT_BLOCK, ValueTypes.NBT).output(ValueTypes.OBJECT_BLOCK)\n            .symbol(\"block_with_props\").operatorName(\"blockfromproperties\").interactName(\"withProperties\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                ValueTypeNbt.ValueNbt b = variables.getValue(1, ValueTypes.NBT);\n                if (a.getRawValue().isPresent() && a.getRawValue().isPresent()) {\n                    BlockState blockState = a.getRawValue().get();\n                    Tag tagRaw = b.getRawValue().get();\n                    if (tagRaw instanceof CompoundTag) {\n                        CompoundTag tag = (CompoundTag) tagRaw;\n                        for (Property property : blockState.getProperties()) {\n                            if (tag.contains(property.getName())) {\n                                Optional<Comparable> valueOptional = property.getValue(tag.getString(property.getName()).orElseThrow());\n                                if (valueOptional.isPresent()) {\n                                    blockState = blockState.setValue(property, valueOptional.get());\n                                }\n                            }\n                        }\n                        return ValueObjectTypeBlock.ValueBlock.of(blockState);\n                    }\n                }\n                return a;\n            }).build());\n\n    /**\n     * Get all possible block properties as NBT compound tag with list values.\n     */\n    public static final IOperator OBJECT_BLOCK_POSSIBLE_PROPERTIES = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG\n            .output(ValueTypes.NBT)\n            .symbol(\"block_all_props\").operatorName(\"blockpossibleproperties\").interactName(\"possibleProperties\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                return ValueTypeNbt.ValueNbt.of(a.getRawValue().map(blockState -> {\n                    CompoundTag tag = new CompoundTag();\n                    for (Property property : blockState.getProperties()) {\n                        ListTag list = new ListTag();\n                        for (Comparable value : (Collection<Comparable>) property.getPossibleValues()) {\n                            list.add(StringTag.valueOf(property.getName(value)));\n                        }\n                        tag.put(property.getName(), list);\n                    }\n                    return tag;\n                }));\n            }).build());\n\n    /**\n     * The tag entries of the given block\n     */\n    public static final IOperator OBJECT_BLOCK_TAG = REGISTRY.register(OperatorBuilders.BLOCK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST)\n            .symbol(\"block_tag_names\").operatorName(\"tag\").interactName(\"tags\")\n            .function(variables -> {\n                ValueObjectTypeBlock.ValueBlock a = variables.getValue(0, ValueTypes.OBJECT_BLOCK);\n                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();\n                if(!a.getRawValue().isEmpty()) {\n                    a.getRawValue().get().getBlock().builtInRegistryHolder().tags()\n                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString\n                                    .of(owningTag.location().toString())));\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());\n            }).build());\n\n    /**\n     * Get a list of blocks that correspond to the given tag key.\n     */\n    public static final IOperator OBJECT_BLOCK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX\n            .output(ValueTypes.LIST)\n            .symbol(\"block_tag_values\").operatorName(\"blocktag\").interactName(\"blocksByTag\")\n            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)\n            .function(variables -> {\n                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);\n                ImmutableList.Builder<ValueObjectTypeBlock.ValueBlock> builder = ImmutableList.builder();\n                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {\n                    try {\n                        Helpers.getBlockTagValues(a.getRawValue())\n                                .map(ValueObjectTypeBlock.ValueBlock::of)\n                                .forEach(builder::add);\n                    } catch (IdentifierException e) {\n                        throw new EvaluationException(Component.translatable(e.getMessage()));\n                    }\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_BLOCK, builder.build());\n            }).build());\n\n    /**\n     * ----------------------------------- ITEM STACK OBJECT OPERATORS -----------------------------------\n     */\n\n    /**\n     * Item Stack size operator with one input itemstack and one output integer.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_SIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperator(\"size\").interactName(\"size\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(\n                itemStack -> !itemStack.isEmpty() ? itemStack.getCount() : 0\n            )).build());\n\n    /**\n     * Item Stack maxsize operator with one input itemstack and one output integer.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_MAXSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperator(\"maxsize\").interactName(\"maxSize\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(\n                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxStackSize() : 0\n            )).build());\n\n    /**\n     * Item Stack isstackable operator with one input itemstack and one output boolean.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISSTACKABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbolOperator(\"stackable\").interactName(\"isStackable\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(\n                itemStack -> !itemStack.isEmpty() && itemStack.isStackable()\n            )).build());\n\n    /**\n     * Item Stack isdamageable operator with one input itemstack and one output boolean.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISDAMAGEABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbolOperator(\"damageable\").interactName(\"isDamageable\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(\n                itemStack -> !itemStack.isEmpty() && itemStack.isDamageableItem()\n            )).build());\n\n    /**\n     * Item Stack damage operator with one input itemstack and one output integer.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_DAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperator(\"damage\").interactName(\"damage\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(\n                itemStack -> !itemStack.isEmpty() ? itemStack.getDamageValue() : 0\n            )).build());\n\n    /**\n     * Item Stack maxdamage operator with one input itemstack and one output integer.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_MAXDAMAGE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbol(\"max_damage\").operatorName(\"maxdamage\").interactName(\"maxDamage\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(\n                itemStack -> !itemStack.isEmpty() ? itemStack.getMaxDamage() : 0\n            )).build());\n\n    /**\n     * Item Stack isenchanted operator with one input itemstack and one output boolean.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTED = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbolOperator(\"enchanted\").interactName(\"isEnchanted\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(\n                itemStack -> !itemStack.isEmpty() && itemStack.isEnchanted()\n            )).build());\n\n    /**\n     * Item Stack isenchantable operator with one input itemstack and one output boolean.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISENCHANTABLE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbolOperator(\"enchantable\").interactName(\"isEnchantable\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(\n                itemStack -> !itemStack.isEmpty() && itemStack.isEnchantable()\n            )).build());\n\n    /**\n     * Item Stack repair cost with one input itemstack and one output integer.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_REPAIRCOST = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER)\n            .symbol(\"repair_cost\").operatorName(\"repaircost\").interactName(\"repairCost\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(\n                itemStack -> !itemStack.isEmpty() ? itemStack.get(DataComponents.REPAIR_COST) : 0\n            )).build());\n\n    /**\n     * Get the rarity of an itemstack.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_RARITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).symbolOperatorInteract(\"rarity\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                return ValueTypeString.ValueString.of(!a.getRawValue().isEmpty() ? a.getRawValue().getRarity().name() : \"\");\n            }).build());\n\n    /**\n     * Get the strength of an itemstack against a block as a double.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2\n            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.DOUBLE)\n            .symbolOperatorInteract(\"strength\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);\n                return ValueTypeDouble.ValueDouble.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() ? a.getRawValue().getDestroySpeed(b.getRawValue().get()) : 0);\n            }).build());\n\n    /**\n     * If the given itemstack can be used to harvest the given block.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG\n            .inputTypes(new IValueType[]{ValueTypes.OBJECT_ITEMSTACK, ValueTypes.OBJECT_BLOCK}).output(ValueTypes.BOOLEAN)\n            .symbol(\"can_harvest\").operatorName(\"canharvest\").interactName(\"canHarvest\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ValueObjectTypeBlock.ValueBlock b = variables.getValue(1, ValueTypes.OBJECT_BLOCK);\n                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && b.getRawValue().isPresent() && a.getRawValue().isCorrectToolForDrops(b.getRawValue().get()));\n            }).build());\n\n    /**\n     * The block from the stack\n     */\n    public static final IOperator OBJECT_ITEMSTACK_BLOCK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract(\"block\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                return ValueObjectTypeBlock.ValueBlock.of((!a.getRawValue().isEmpty() && a.getRawValue().getItem() instanceof BlockItem) ? IModHelpers.get().getBlockHelpers().getBlockStateFromItemStack(a.getRawValue()) : null);\n            }).build());\n\n    /**\n     * If the given stack has a fluid.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISFLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN)\n            .symbol(\"is_fluidstack\").operatorName(\"isfluidstack\").interactName(\"isFluidStack\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(\n                itemStack -> !itemStack.isEmpty() && !Helpers.getFluidStack(itemStack).isEmpty()\n            )).build());\n\n    /**\n     * The fluidstack from the stack\n     */\n    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACK = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator(\"fluidstack\").interactName(\"fluidStack\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                return ValueObjectTypeFluidStack.ValueFluidStack.of(!a.getRawValue().isEmpty() ? Helpers.getFluidStack(a.getRawValue()) : FluidStack.EMPTY);\n            }).build());\n\n    /**\n     * The capacity of the fluidstack from the stack.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LONG)\n            .symbol(\"fluidstack_capacity\").operatorName(\"fluidstackcapacity\").interactName(\"fluidCapacity\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_LONG.build(\n                itemStack -> !itemStack.isEmpty() ? Helpers.getFluidStackCapacity(itemStack) : 0\n            )).build());\n\n    /**\n     * If the data components of the given stacks are equal.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISDATAEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2\n            .output(ValueTypes.BOOLEAN).symbol(\"=NBT=\").operatorName(\"isnbtequal\").interactName(\"isNbtEqual\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);\n                ItemStack a = valueStack0.getRawValue();\n                ItemStack b = valueStack1.getRawValue();\n                boolean equal = false;\n                if(!a.isEmpty() && !b.isEmpty()) {\n                    equal = ItemStack.isSameItem(a, b) && ItemMatch.areItemStacksEqual(a, b, ItemMatch.DATA);\n                } else if(a.isEmpty() && b.isEmpty()) {\n                    equal = true;\n                }\n                return ValueTypeBoolean.ValueBoolean.of(equal);\n            }).build());\n\n    /**\n     * If the raw items of the given stacks are equal, ignoring data components and item count but including damage value.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISITEMEQUALNODATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_2\n            .output(ValueTypes.BOOLEAN).symbol(\"=NoNBT=\").operatorName(\"isitemequalnonbt\").interactName(\"isEqualNonNbt\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);\n                ItemStack a = valueStack0.getRawValue();\n                ItemStack b = valueStack1.getRawValue();\n                boolean equal = false;\n                if(!a.isEmpty() && !b.isEmpty()) {\n                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);\n                } else if(a.isEmpty() && b.isEmpty()) {\n                    equal = true;\n                }\n                return ValueTypeBoolean.ValueBoolean.of(equal);\n            }).build());\n\n    /**\n     * If the raw items of the given stacks are equal, ignoring data components, item count, and damage value.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISRAWITEMEQUAL = REGISTRY.register(OperatorBuilders.ITEMSTACK_2\n            .output(ValueTypes.BOOLEAN).symbol(\"=Raw=\").operatorName(\"israwitemequal\").interactName(\"isEqualRaw\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack valueStack0 = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ValueObjectTypeItemStack.ValueItemStack valueStack1 = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);\n                ItemStack a = valueStack0.getRawValue();\n                ItemStack b = valueStack1.getRawValue();\n                boolean equal = false;\n                if(!a.isEmpty() && !b.isEmpty()) {\n                    equal = ItemMatch.areItemStacksEqual(a, b, ItemMatch.ITEM);\n                } else if(a.isEmpty() && b.isEmpty()) {\n                    equal = true;\n                }\n                return ValueTypeBoolean.ValueBoolean.of(equal);\n            }).build());\n\n    /**\n     * The name of the mod owning this item\n     */\n    public static final IOperator OBJECT_ITEMSTACK_MODNAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbolOperatorInteract(\"mod\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    (OperatorBase.SafeVariablesGetter variables) -> {\n                        ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                        return !a.getRawValue().isEmpty() ? BuiltInRegistries.ITEM.getKey(a.getRawValue().getItem()) : Identifier.parse(\"\");\n                    },\n                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME\n            ))).build());\n\n    /**\n     * The fuel burn time of the given item\n     */\n    public static final IOperator OBJECT_ITEMSTACK_FUELBURNTIME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER)\n            .symbol(\"burn_time\").operatorName(\"burntime\").interactName(\"burnTime\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_INT.build(itemStack -> {\n                if (!itemStack.isEmpty()) {\n                    int burnTime = itemStack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues());\n                    return EventHooks.getItemBurnTime(itemStack, burnTime == -1\n                            ? itemStack.getBurnTime(RecipeType.SMELTING, ServerLifecycleHooks.getCurrentServer().fuelValues())\n                            : burnTime, null, ServerLifecycleHooks.getCurrentServer().fuelValues());\n                }\n                return 0;\n            })).build());\n\n    /**\n     * If the given item can be used as fuel\n     */\n    public static final IOperator OBJECT_ITEMSTACK_CANBURN = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN)\n            .symbol(\"can_burn\").operatorName(\"canburn\").interactName(\"canBurn\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN\n                    .build(stack -> stack.getBurnTime(null, ServerLifecycleHooks.getCurrentServer().fuelValues()) > 0))\n            .build());\n\n    /**\n     * The tag entries of the given item\n     */\n    public static final IOperator OBJECT_ITEMSTACK_TAG = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST)\n            .symbol(\"item_tag_names\").operatorName(\"tag\").interactName(\"tags\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();\n                if(!a.getRawValue().isEmpty()) {\n                    a.getRawValue().getItem().builtInRegistryHolder().tags()\n                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString\n                                    .of(owningTag.location().toString())));\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());\n            }).build());\n\n    /**\n     * Get a list of items that correspond to the given tag key.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX\n            .output(ValueTypes.LIST)\n            .symbol(\"item_tag_values\").operatorName(\"tag\").interactName(\"itemsByTag\")\n            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)\n            .function(variables -> {\n                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);\n                ImmutableList.Builder<ValueObjectTypeItemStack.ValueItemStack> builder = ImmutableList.builder();\n                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {\n                    try {\n                        Helpers.getTagValues(a.getRawValue())\n                                .map(ValueObjectTypeItemStack.ValueItemStack::of)\n                                .forEach(builder::add);\n                    } catch (IdentifierException e) {\n                        throw new EvaluationException(Component.translatable(e.getMessage()));\n                    }\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, builder.build());\n            }).build());\n\n    /**\n     * ItemStack operator that applies the given stacksize to the given itemstack and creates a new ItemStack.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_WITHSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_INTEGER_1\n            .output(ValueTypes.OBJECT_ITEMSTACK)\n            .symbol(\"with_size\").operatorName(\"withsize\").interactName(\"withSize\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ValueTypeInteger.ValueInteger b = variables.getValue(1, ValueTypes.INTEGER);\n                if (!a.getRawValue().isEmpty()) {\n                    ItemStack itemStack = a.getRawValue().copy();\n                    itemStack.setCount(b.getRawValue());\n                    return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);\n                }\n                return a;\n            }).build());\n\n    /**\n     * Check if the item is an energy container item\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ISENERGYCONTAINER = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN)\n            .symbol(\"is_energy_container\").operatorName(\"isenergycontainer\").interactName(\"isEnergyContainer\")\n            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_BOOLEAN.build(\n                Objects::nonNull\n            )).build());\n\n    /**\n     * Get the storage energy\n     */\n    public static final IOperator OBJECT_ITEMSTACK_STOREDENERGY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LONG)\n            .symbol(\"stored_energy\").operatorName(\"storedenergy\").interactName(\"energyStored\")\n            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_LONG.build(\n                input -> input != null ? input.getAmountAsLong() : 0\n            )).build());\n\n    /**\n     * Get the energy capacity\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ENERGYCAPACITY = Operators.REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LONG)\n            .symbol(\"capacity_energy\").operatorName(\"energycapacity\").interactName(\"energyCapacity\")\n            .function(OperatorBuilders.FUNCTION_CONTAINERITEM_TO_LONG.build(\n                input -> input != null ? input.getCapacityAsLong() : 0\n            )).build());\n\n\n    /**\n     * If the given item has an inventory.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_HASINVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN)\n            .symbol(\"has_inventory\").operatorName(\"hasinventory\").interactName(\"hasInventory\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                return ValueTypeBoolean.ValueBoolean.of(!a.getRawValue().isEmpty() && ItemAccess.forStack(a.getRawValue()).getCapability(Capabilities.Item.ITEM) != null);\n            }).build());\n\n\n\n\n    /**\n     * Retrieve the inventory size of the given item handler contents.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_INVENTORYSIZE = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER)\n            .symbol(\"inventory_size\").operatorName(\"inventorysize\").interactName(\"inventorySize\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ResourceHandler<ItemResource> itemHandler = a.getRawValue().isEmpty() ? null : ItemAccess.forStack(a.getRawValue()).getCapability(Capabilities.Item.ITEM);\n                return ValueTypeInteger.ValueInteger.of(itemHandler != null ? itemHandler.size() : 0);\n            }).build());\n\n    /**\n     * Retrieve the inventory of the given item handler contents.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_INVENTORY = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbolOperator(\"inventory\").interactName(\"inventory\")\n            .function(variables -> {\n                ValueObjectTypeItemStack.ValueItemStack a = variables.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                ResourceHandler<ItemResource> itemHandler = a.getRawValue().isEmpty() ? null : ItemAccess.forStack(a.getRawValue()).getCapability(Capabilities.Item.ITEM);\n                if (itemHandler != null) {\n                    List<ValueObjectTypeItemStack.ValueItemStack> values = Lists.newArrayListWithCapacity(itemHandler.size());\n                    for (int i = 0; i < itemHandler.size(); i++) {\n                        values.add(ValueObjectTypeItemStack.ValueItemStack.of(itemHandler.getResource(i).toStack(itemHandler.getAmountAsInt(i))));\n                    }\n                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, values);\n                }\n                return ValueTypes.LIST.getDefault();\n            }).build());\n\n    /**\n     * Get an item by name.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG\n            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_ITEMSTACK)\n            .symbol(\"item_by_name\").operatorName(\"itembyname\").interactName(\"itemByName\")\n            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION\n                    .build(input -> {\n                        Item item = BuiltInRegistries.ITEM.getValue(input);\n                        ItemStack itemStack = ItemStack.EMPTY;\n                        if (item != null) {\n                            itemStack = new ItemStack(item);\n                        }\n                        return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);\n                    })).build());\n\n    /**\n     * Get the total item count of the given item in a list.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_LIST_COUNT = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG\n            .inputTypes(ValueTypes.LIST, ValueTypes.OBJECT_ITEMSTACK)\n            .output(ValueTypes.INTEGER)\n            .symbol(\"item_list_count\").operatorName(\"itemlistcount\").interactName(\"itemListCount\")\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList<IValueType<IValue>, IValue> a = variables.getValue(0, ValueTypes.LIST);\n                    ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);\n                    if (!ValueHelpers.correspondsTo(a.getRawValue().getValueType(), ValueTypes.OBJECT_ITEMSTACK)) {\n                        MutableComponent error = Component.translatable(\n                                L10NValues.VALUETYPE_ERROR_INVALIDLISTVALUETYPE,\n                                Component.translatable(a.getRawValue().getValueType().getTranslationKey()),\n                                Component.translatable(ValueTypes.OBJECT_ITEMSTACK.getTranslationKey()));\n                        throw new EvaluationException(error);\n                    }\n\n                    ItemStack itemStack = b.getRawValue();\n                    int count = 0;\n                    if (a.getRawValue().isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                OBJECT_ITEMSTACK_LIST_COUNT.getLocalizedNameFull()));\n                    }\n                    for (IValue listValueRaw : a.getRawValue()) {\n                        if (listValueRaw.getType().correspondsTo(ValueTypes.OBJECT_ITEMSTACK)) {\n                            ValueObjectTypeItemStack.ValueItemStack listValue = (ValueObjectTypeItemStack.ValueItemStack) listValueRaw;\n                            if (!listValue.getRawValue().isEmpty()) {\n                                ItemStack listItem = listValue.getRawValue();\n                                if (!itemStack.isEmpty()) {\n                                    if (ItemStack.isSameItemSameComponents(itemStack, listItem)) {\n                                        count += listItem.getCount();\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    return ValueTypeInteger.ValueInteger.of(count);\n                }\n            }).build());\n\n    /**\n     * Item Stack size operator with one input itemstack and one output NBT tag.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.NBT).symbol(\"NBT()\").operatorName(\"nbt\").interactName(\"nbt\")\n            .function(input -> {\n                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty\n                if (itemStack.getRawValue().isEmpty() || itemStack.getRawValue().getComponents().isEmpty()) {\n                    return ValueTypeNbt.ValueNbt.of();\n                }\n                CompoundTag tag = (CompoundTag) DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), itemStack.getRawValue().getComponentsPatch()).getOrThrow();\n                // Remove entries that have been marked as removed.\n                for (String key : Sets.newHashSet(tag.keySet())) {\n                    if (key.startsWith(\"!\")) {\n                        tag.remove(key);\n                    }\n                }\n                if (tag.isEmpty()) {\n                    return ValueTypeNbt.ValueNbt.of();\n                }\n                return ValueTypeNbt.ValueNbt.of(Optional.of(tag));\n            }).build());\n\n    /**\n     * Item Stack has_nbt operator with one input itemstack and one output boolean.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_HASDATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_PREFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"has_nbt\").operatorName(\"hasnbt\").interactName(\"hasNbt\")\n            .function(OperatorBuilders.FUNCTION_ITEMSTACK_TO_BOOLEAN.build(\n                    itemStack -> !itemStack.isEmpty() && itemStack.getComponents().stream().anyMatch(t -> !t.type().isTransient())\n            )).build());\n\n    /**\n     * Get the data component keys of an itemstack.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbol(\"data_keys\").operatorName(\"datakeys\").interactName(\"dataKeys\")\n            .function(input -> {\n                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                // Explicitly check for item emptiness first, because vanilla sometimes persists NBT when setting stacks to empty\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,\n                        itemStack.getRawValue().isEmpty() ?\n                                Lists.newArrayList() :\n                                itemStack.getRawValue().getComponents().keySet().stream()\n                                        .filter(c -> !c.isTransient())\n                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))\n                                        .sorted(Comparator.comparing(ValueTypeString.ValueString::getRawValue))\n                                        .toList());\n            }).build());\n\n    /**\n     * Get the data component value by key\n     */\n    public static final IOperator OBJECT_ITEMSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.ITEMSTACK_2_LONG\n            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING)\n            .output(ValueTypes.NBT).symbol(\"data_value\").operatorName(\"datavalue\").interactName(\"dataValue\")\n            .function(input -> {\n                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n\n                // Determine data component type\n                DataComponentType<?> dataComponentType;\n                try {\n                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));\n                } catch (IdentifierException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n\n                // Fetch component value\n                TypedDataComponent<?> typedComponent = itemStack.getRawValue().getComponents().getTyped(dataComponentType);\n                if (typedComponent == null) {\n                    return ValueTypeNbt.ValueNbt.of((Tag) null);\n                }\n\n                // Encode component value\n                try {\n                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();\n                    return ValueTypeNbt.ValueNbt.of(tag);\n                } catch (IllegalStateException e) {\n                    return ValueTypeNbt.ValueNbt.of((Tag) null);\n                }\n            }).build());\n\n    /**\n     * Get the data component value by key\n     */\n    public static final IOperator OBJECT_ITEMSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.ITEMSTACK_3\n            .inputTypes(ValueTypes.OBJECT_ITEMSTACK, ValueTypes.STRING, ValueTypes.NBT)\n            .output(ValueTypes.NBT).symbol(\"with_data\").operatorName(\"withdata\").interactName(\"withData\")\n            .function(input -> {\n                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n\n                // Skip further processing if input value is empty\n                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();\n                if (!tagOptional.isPresent()) {\n                    return itemStack;\n                }\n\n                // Determine data component type\n                DataComponentType<?> dataComponentType;\n                try {\n                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));\n                } catch (IdentifierException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n\n                // Encode component value\n                try {\n                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();\n                    itemStack = ValueObjectTypeItemStack.ValueItemStack.of(itemStack.getRawValue().copy());\n                    itemStack.getRawValue().set((DataComponentType) dataComponentType, value);\n                    return itemStack;\n                } catch (IllegalStateException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n            }).build());\n\n    /**\n     * Get the tooltip of an itemstack in list form.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_TOOLTIP = REGISTRY.register(OperatorBuilders.ITEMSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbol(\"tooltip\").operatorName(\"tooltip\").interactName(\"tooltip\")\n            .function(input -> {\n                ValueObjectTypeItemStack.ValueItemStack itemStack = input.getValue(0, ValueTypes.OBJECT_ITEMSTACK);\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,\n                        itemStack.getRawValue().getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.ADVANCED).stream()\n                                .map(c -> ValueTypeString.ValueString.of(c.getString()))\n                                .toList());\n            }).build());\n    /**\n     * Get the tooltip of an itemstack in list form, using the provided player entity as the player context.\n     */\n    public static final IOperator OBJECT_ITEMSTACK_ENTITY_TOOLTIP = REGISTRY.register(OperatorBuilders.ENTITY_1_ITEMSTACK_1\n            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)\n            .output(ValueTypes.LIST).symbol(\"entity_item_tooltip\").operatorName(\"entityitemtooltip\").interactName(\"entityItemTooltip\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);\n                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {\n                    Player entity = (Player) a.getRawValue().get();\n                    return ValueTypeList.ValueList.ofList(ValueTypes.STRING,\n                            itemStack.getRawValue().getTooltipLines(Item.TooltipContext.of(entity.level()), entity, TooltipFlag.Default.ADVANCED).stream()\n                                    .map(c -> ValueTypeString.ValueString.of(c.getString()))\n                                    .toList());\n                }\n                return ValueTypes.LIST.getDefault();\n            }).build());\n\n    /**\n     * ----------------------------------- ENTITY OBJECT OPERATORS -----------------------------------\n     */\n\n    /**\n     * If the entity is a mob\n     */\n    public static final IOperator OBJECT_ENTITY_ISMOB = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_mob\").operatorName(\"ismob\").interactName(\"isMob\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity instanceof Enemy\n            )).build());\n\n    /**\n     * If the entity is an animal\n     */\n    public static final IOperator OBJECT_ENTITY_ISANIMAL = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_animal\").operatorName(\"isanimal\").interactName(\"isAnimal\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity instanceof Animal && !(entity instanceof Enemy)\n            )).build());\n\n    /**\n     * If the entity is an item\n     */\n    public static final IOperator OBJECT_ENTITY_ISITEM = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_item\").operatorName(\"isitem\").interactName(\"isItem\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity instanceof ItemEntity\n            )).build());\n\n    /**\n     * If the entity is a player\n     */\n    public static final IOperator OBJECT_ENTITY_ISPLAYER = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_player\").operatorName(\"isplayer\").interactName(\"isPlayer\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity instanceof Player\n            )).build());\n\n    /**\n     * If the entity is a minecart\n     */\n    public static final IOperator OBJECT_ENTITY_ISMINECART = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_minecart\").operatorName(\"isminecart\").interactName(\"isMinecart\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity instanceof AbstractMinecart\n            )).build());\n\n    /**\n     * The itemstack from the entity\n     */\n    public static final IOperator OBJECT_ENTITY_ITEMSTACK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX\n            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract(\"item\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                return ValueObjectTypeItemStack.ValueItemStack.of((a.isPresent() && a.get() instanceof ItemEntity) ? ((ItemEntity) a.get()).getItem() : ItemStack.EMPTY);\n            }).build());\n\n    /**\n     * The entity health\n     */\n    public static final IOperator OBJECT_ENTITY_HEALTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.DOUBLE).symbolOperatorInteract(\"health\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(\n                entity -> entity instanceof LivingEntity ? ((LivingEntity) entity).getHealth() : 0.0\n            )).build());\n\n    /**\n     * The entity width\n     */\n    public static final IOperator OBJECT_ENTITY_WIDTH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.DOUBLE).symbolOperatorInteract(\"width\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(\n                entity -> entity != null ? entity.getBbWidth() : 0.0\n            )).build());\n\n    /**\n     * The entity width\n     */\n    public static final IOperator OBJECT_ENTITY_HEIGHT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.DOUBLE).symbolOperatorInteract(\"height\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_DOUBLE.build(\n                entity -> entity != null ? entity.getBbHeight() : 0.0\n            )).build());\n\n    /**\n     * If the entity is burning\n     */\n    public static final IOperator OBJECT_ENTITY_ISBURNING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_burning\").operatorName(\"isburning\").interactName(\"entityIsBurning\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity != null && entity.isOnFire()\n            )).build());\n\n    /**\n     * If the entity is wet\n     */\n    public static final IOperator OBJECT_ENTITY_ISWET = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_wet\").operatorName(\"iswet\").interactName(\"isWet\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity != null && entity.isInWaterOrRain()\n            )).build());\n\n    /**\n     * If the entity is crouching\n     */\n    public static final IOperator OBJECT_ENTITY_ISCROUCHING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_crouching\").operatorName(\"iscrouching\").interactName(\"isCrouching\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity != null && entity.isCrouching()\n            )).build());\n\n    /**\n     * If the entity is eating\n     */\n    public static final IOperator OBJECT_ENTITY_ISEATING = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbol(\"is_eating\").operatorName(\"iseating\").interactName(\"isEating\")\n            .function(OperatorBuilders.FUNCTION_ENTITY_TO_BOOLEAN.build(\n                entity -> entity instanceof LivingEntity && ((LivingEntity) entity).getUseItemRemainingTicks() > 0\n            )).build());\n\n    /**\n     * The list of armor itemstacks from an entity\n     */\n    public static final IOperator OBJECT_ENTITY_ARMORINVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbol(\"armor_inventory\").operatorName(\"armorinventory\").interactName(\"armorInventory\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                if(a.isPresent()) {\n                    Entity entity = a.get();\n                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityArmorInventory(entity.level(), entity));\n                } else {\n                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());\n                }\n            }).build());\n\n    /**\n     * The list of itemstacks from an entity\n     */\n    public static final IOperator OBJECT_ENTITY_INVENTORY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbolOperator(\"inventory\").interactName(\"inventory\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                if(a.isPresent()) {\n                    Entity entity = a.get();\n                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityInventory(entity.level(), entity));\n                } else {\n                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.<ValueObjectTypeItemStack.ValueItemStack>emptyList());\n                }\n            }).build());\n\n    /**\n     * The name of the mod owning this entity\n     */\n    public static final IOperator OBJECT_ENTITY_MODNAME = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbolOperatorInteract(\"mod\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    (OperatorBase.SafeVariablesGetter variables) -> {\n                        ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                        if(a.getRawValue().isPresent()) {\n                            Entity entity = a.getRawValue().get();\n                            return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());\n                        }\n                        return Identifier.parse(\"\");\n                    },\n                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME\n            )))\n            .build());\n\n    /**\n     * The block the given player is currently looking at.\n     */\n    public static final IOperator OBJECT_PLAYER_TARGETBLOCK = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_BLOCK)\n            .symbol(\"target_block\").operatorName(\"targetblock\").interactName(\"targetBlock\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                BlockState blockState = null;\n                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    LivingEntity entity = (LivingEntity) a.getRawValue().get();\n                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.BLOCK_INTERACTION_RANGE);\n                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();\n                    double eyeHeight = entity.getEyeHeight();\n                    Vec3 lookVec = entity.getLookAngle();\n                    Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());\n                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);\n\n                    ClipContext rayTraceContext = new ClipContext(origin, direction, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity);\n                    BlockHitResult mop = entity.level().clip(rayTraceContext);\n                    if(mop != null && mop.getType() == HitResult.Type.BLOCK) {\n                        blockState = entity.level().getBlockState(mop.getBlockPos());\n                    }\n                }\n                return ValueObjectTypeBlock.ValueBlock.of(blockState);\n            }).build());\n\n    /**\n     * The entity the given player is currently looking at.\n     */\n    public static final IOperator OBJECT_PLAYER_TARGETENTITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ENTITY)\n            .symbol(\"target_entity\").operatorName(\"targetentity\").interactName(\"targetEntity\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Entity entityOut = null;\n                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    LivingEntity entity = (LivingEntity) a.getRawValue().get();\n                    AttributeInstance reachDistanceAttribute = entity.getAttribute(Attributes.ENTITY_INTERACTION_RANGE);\n                    double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();\n\n                    // Copied and modified from GameRenderer#getMouseOver\n                    Vec3 origin = entity.getEyePosition(1.0F);\n                    double reachDistanceSquared = reachDistance * reachDistance;\n\n                    Vec3 lookVec = entity.getViewVector(1.0F);\n                    Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);\n                    AABB boundingBox = entity.getBoundingBox()\n                            .expandTowards(lookVec.scale(reachDistance))\n                            .inflate(1.0D, 1.0D, 1.0D);\n                    EntityHitResult entityraytraceresult = ProjectileUtil.getEntityHitResult(entity, origin, direction,\n                            boundingBox, e -> !e.isSpectator() && e.isPickable(), reachDistanceSquared);\n                    if (entityraytraceresult != null) {\n                        Entity entity1 = entityraytraceresult.getEntity();\n                        Vec3 vec3d3 = entityraytraceresult.getLocation();\n                        double distanceSquared = origin.distanceToSqr(vec3d3);\n                        if (distanceSquared < reachDistanceSquared\n                                && (entity1 instanceof LivingEntity || entity1 instanceof ItemFrame)) {\n                            entityOut = entity1;\n                        }\n                    }\n                }\n                return ValueObjectTypeEntity.ValueEntity.of(entityOut);\n            }).build());\n\n    /**\n     * If the given player has an external gui open.\n     */\n    public static final IOperator OBJECT_PLAYER_HASGUIOPEN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)\n            .symbol(\"has_gui_open\").operatorName(\"hasguiopen\").interactName(\"hasGuiOpen\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof Player) {\n                    Player entity = (Player) a.getRawValue().get();\n                    return ValueTypeBoolean.ValueBoolean.of(entity.containerMenu != entity.inventoryMenu);\n                }\n                return ValueTypeBoolean.ValueBoolean.of(false);\n            }).build());\n\n    /**\n     * The item the given entity is currently holding in its main hand.\n     */\n    public static final IOperator OBJECT_ENTITY_HELDITEM_MAIN = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)\n            .symbol(\"held_item_1\").operatorName(\"helditem\").interactName(\"heldItem\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                ItemStack itemStack = ItemStack.EMPTY;\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    itemStack = ((LivingEntity) a.getRawValue().get()).getMainHandItem();\n                }\n                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);\n            }).build());\n\n    /**\n     * The item the given entity is currently holding in its off hand.\n     */\n    public static final IOperator OBJECT_ENTITY_HELDITEM_OFF = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)\n            .symbol(\"held_item_2\").operatorName(\"helditemoffhand\").interactName(\"heldItemOffHand\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                ItemStack itemStack = ItemStack.EMPTY;\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    itemStack = ((LivingEntity) a.getRawValue().get()).getOffhandItem();\n                }\n                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);\n            }).build());\n\n    /**\n     * The entity's mounted entity\n     */\n    public static final IOperator OBJECT_ENTITY_MOUNTED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.LIST)\n            .symbolOperator(\"mounted\").interactName(\"mounted\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                List<ValueObjectTypeEntity.ValueEntity> passengers = Lists.newArrayList();\n                if(a.getRawValue().isPresent()) {\n                    for (Entity passenger : a.getRawValue().get().getPassengers()) {\n                        passengers.add(ValueObjectTypeEntity.ValueEntity.of(passenger));\n                    }\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, passengers);\n            }).build());\n\n    /**\n     * The item frame's contents\n     */\n    public static final IOperator OBJECT_ITEMFRAME_CONTENTS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.OBJECT_ITEMSTACK)\n            .symbol(\"itemframe_contents\").operatorName(\"itemframecontents\").interactName(\"itemFrameContents\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                ItemStack itemStack = ItemStack.EMPTY;\n                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {\n                    itemStack = ((ItemFrame) a.getRawValue().get()).getItem();\n                }\n                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);\n            }).build());\n\n    /**\n     * The item frame's rotation\n     */\n    public static final IOperator OBJECT_ITEMFRAME_ROTATION = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)\n            .symbol(\"itemframe_rotation\").operatorName(\"itemframerotation\").interactName(\"itemFrameRotation\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Integer rotation = 0;\n                if(a.getRawValue().isPresent() && a.getRawValue().get() instanceof ItemFrame) {\n                    rotation = ((ItemFrame) a.getRawValue().get()).getRotation();\n                }\n                return ValueTypeInteger.ValueInteger.of(rotation);\n            }).build());\n\n    /**\n     * The hurtsound of this entity.\n     */\n    public static final IOperator OBJECT_ENTITY_HURTSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbolOperator(\"hurtsound\").interactName(\"hurtSound\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                String hurtSound = \"\";\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    String sound = ((LivingEntity) a.getRawValue().get()).getHurtSound(a.getRawValue().get().damageSources().generic()).location().toString();\n                    if (sound != null) {\n                        hurtSound = sound;\n                    }\n                }\n                return ValueTypeString.ValueString.of(hurtSound);\n            }).build());\n\n    /**\n     * The deathsound of this entity.\n     */\n    public static final IOperator OBJECT_ENTITY_DEATHSOUND = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbolOperator(\"deathsound\").interactName(\"deathSound\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                String hurtSound = \"\";\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    String sound = ((LivingEntity) a.getRawValue().get()).getDeathSound().location().toString();\n                    if (sound != null) {\n                        hurtSound = sound;\n                    }\n                }\n                return ValueTypeString.ValueString.of(hurtSound);\n            }).build());\n\n    /**\n     * The age of this entity.\n     */\n    public static final IOperator OBJECT_ENTITY_AGE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.INTEGER)\n            .symbolOperatorInteract(\"age\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                int age = 0;\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    age = ((LivingEntity) a.getRawValue().get()).getNoActionTime();\n                }\n                return ValueTypeInteger.ValueInteger.of(age);\n            }).build());\n\n    /**\n     * If the entity is a child.\n     */\n    public static final IOperator OBJECT_ENTITY_ISCHILD = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)\n            .symbol(\"is_child\").operatorName(\"ischild\").interactName(\"isChild\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                boolean child = false;\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof LivingEntity) {\n                    child = ((LivingEntity) a.getRawValue().get()).isBaby();\n                }\n                return ValueTypeBoolean.ValueBoolean.of(child);\n            }).build());\n\n    /**\n     * If the entity can be bred.\n     */\n    public static final IOperator OBJECT_ENTITY_CANBREED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)\n            .symbol(\"canbreed\").operatorName(\"canbreed\").interactName(\"canBreed\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                boolean canBreed = false;\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof AgeableMob) {\n                    canBreed = ((AgeableMob) a.getRawValue().get()).getAge() == 0;\n                }\n                return ValueTypeBoolean.ValueBoolean.of(canBreed);\n            }).build());\n\n    /**\n     * If the entity is in love.\n     */\n    public static final IOperator OBJECT_ENTITY_ISINLOVE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)\n            .symbol(\"is_in_love\").operatorName(\"isinlove\").interactName(\"isInLove\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                boolean inLove = false;\n                if (a.getRawValue().isPresent() && a.getRawValue().get() instanceof Animal) {\n                    inLove = ((Animal) a.getRawValue().get()).isInLove();\n                }\n                return ValueTypeBoolean.ValueBoolean.of(inLove);\n            }).build());\n\n    /**\n     * If the entity can be bred with the given item.\n     */\n    public static final IOperator OBJECT_ENTITY_CANBREEDWITH = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .inputTypes(ValueTypes.OBJECT_ENTITY, ValueTypes.OBJECT_ITEMSTACK)\n            .output(ValueTypes.BOOLEAN)\n            .symbol(\"can_breed_with\").operatorName(\"canbreedwith\").interactName(\"canBreedWith\")\n            .renderPattern(IConfigRenderPattern.INFIX_LONG)\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                ValueObjectTypeItemStack.ValueItemStack b = variables.getValue(1, ValueTypes.OBJECT_ITEMSTACK);\n                boolean canBreedWith = false;\n                if (a.getRawValue().isPresent() && !b.getRawValue().isEmpty() && a.getRawValue().get() instanceof Animal) {\n                    canBreedWith = ((Animal) a.getRawValue().get()).isFood(b.getRawValue());\n                }\n                return ValueTypeBoolean.ValueBoolean.of(canBreedWith);\n            }).build());\n\n    /**\n     * If the entity is shearable.\n     */\n    public static final IOperator OBJECT_ENTITY_ISSHEARABLE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG.output(ValueTypes.BOOLEAN)\n            .symbol(\"is_shearable\").operatorName(\"isshearable\").interactName(\"isShearable\")\n            .function(variables -> {\n                ValueObjectTypeEntity.ValueEntity a = variables.getValue(0, ValueTypes.OBJECT_ENTITY);\n                return ValueTypeBoolean.ValueBoolean.of(a.getRawValue().isPresent()\n                        && a.getRawValue().get() instanceof IShearable\n                        && ((IShearable) a.getRawValue().get()).isShearable(null, ItemStack.EMPTY, null, null));\n            }).build());\n\n    /**\n     * The entity serialized to NBT.\n     */\n    public static final IOperator OBJECT_ENTITY_NBT = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.NBT).symbol(\"NBT()\").operatorInteract(\"nbt\")\n            .function(input -> {\n                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                try {\n                    if (entity.getRawValue().isPresent()) {\n                        Entity e = entity.getRawValue().get();\n                        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(e.problemPath(), LOGGER)) {\n                            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, e.level().registryAccess());\n                            e.save(valueOutput);\n                            return ValueTypeNbt.ValueNbt.of(valueOutput.buildResult());\n                        }\n                    }\n                } catch (Exception e) {\n                    // Catch possible errors during NBT writing\n                }\n                return ValueTypes.NBT.getDefault();\n            }).build());\n\n    /**\n     * The entity type.\n     */\n    public static final IOperator OBJECT_ENTITY_TYPE = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).symbol(\"entity_type\").operatorName(\"entitytype\").interactName(\"type\")\n            .function(input -> {\n                ValueObjectTypeEntity.ValueEntity entity = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                String entityType = \"\";\n                if (entity.getRawValue().isPresent()) {\n                    Entity e = entity.getRawValue().get();\n                    entityType = BuiltInRegistries.ENTITY_TYPE.getKey(e.getType()).toString();\n                }\n                return ValueTypeString.ValueString.of(entityType);\n            }).build());\n\n    /**\n     * The entity items.\n     */\n    public static final IOperator OBJECT_ENTITY_ITEMS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbol(\"entity_items\").operatorName(\"entityitems\").interactName(\"items\")\n            .function(input -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                if(a.isPresent()) {\n                    Entity entity = a.get();\n                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityItems(entity.level(), entity, null));\n                } else {\n                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ITEMSTACK, Collections.emptyList());\n                }\n            }).build());\n\n    /**\n     * The entity fluids.\n     */\n    public static final IOperator OBJECT_ENTITY_FLUIDS = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbol(\"entity_fluids\").operatorName(\"entityfluids\").interactName(\"fluids\")\n            .function(input -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                if(a.isPresent()) {\n                    Entity entity = a.get();\n                    return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyEntityFluids(entity.level(), entity, null));\n                } else {\n                    return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Collections.emptyList());\n                }\n            }).build());\n\n    /**\n     * The entity energy stored.\n     */\n    public static final IOperator OBJECT_ENTITY_ENERGY_STORED = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.LONG).symbol(\"entity_stored_energy\").operatorName(\"entityenergystored\").interactName(\"energy\")\n            .function(input -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                if(a.isPresent()) {\n                    Entity entity = a.get();\n                    EnergyHandler energyStorage = entity.getCapability(Capabilities.Energy.ENTITY, null);\n                    return ValueTypeLong.ValueLong.of(energyStorage != null ? energyStorage.getAmountAsLong() : 0);\n                }\n                return ValueTypeLong.ValueLong.of(0);\n            }).build());\n\n    /**\n     * The entity energy stored.\n     */\n    public static final IOperator OBJECT_ENTITY_ENERGY_CAPACITY = REGISTRY.register(OperatorBuilders.ENTITY_1_SUFFIX_LONG\n            .output(ValueTypes.LONG).symbol(\"entity_capacity_energy\").operatorName(\"entityenergycapacity\").interactName(\"energyCapacity\")\n            .function(input -> {\n                ValueObjectTypeEntity.ValueEntity valueEntity = input.getValue(0, ValueTypes.OBJECT_ENTITY);\n                Optional<Entity> a = valueEntity.getRawValue();\n                if(a.isPresent()) {\n                    Entity entity = a.get();\n                    EnergyHandler energyStorage = entity.getCapability(Capabilities.Energy.ENTITY, null);\n                    return ValueTypeLong.ValueLong.of(energyStorage != null ? energyStorage.getCapacityAsLong() : 0);\n                }\n                return ValueTypeLong.ValueLong.of(0);\n            }).build());\n\n    /**\n     * ----------------------------------- FLUID STACK OBJECT OPERATORS -----------------------------------\n     */\n\n    /**\n     * The amount of fluid in the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperatorInteract(\"amount\")\n            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(\n                    FluidStack::getAmount\n            )).build());\n\n    /**\n     * The block from the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_BLOCK = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.OBJECT_BLOCK).symbolOperatorInteract(\"block\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                FluidStack a = valueFluidStack.getRawValue();\n                return ValueObjectTypeBlock.ValueBlock.of(!a.isEmpty() ? a.getFluid().getFluidType().getStateForPlacement(null, null, a).createLegacyBlock() : null);\n            }).build());\n\n    /**\n     * The fluidstack luminosity\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_LIGHT_LEVEL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperator(\"light_level\").interactName(\"lightLevel\")\n            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(\n                fluidStack -> fluidStack.getFluid().getFluidType().getLightLevel(fluidStack)\n            )).build());\n\n    /**\n     * The fluidstack density\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_DENSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperatorInteract(\"density\")\n            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(\n                fluidStack -> fluidStack.getFluid().getFluidType().getDensity(fluidStack)\n            )).build());\n\n    /**\n     * The fluidstack temperature\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_TEMPERATURE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperatorInteract(\"temperature\")\n            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(\n                    fluidStack -> fluidStack.getFluid().getFluidType().getTemperature(fluidStack)\n            )).build());\n\n    /**\n     * The fluidstack viscosity\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_VISCOSITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).symbolOperatorInteract(\"viscosity\")\n            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_INT.build(\n                fluidStack -> fluidStack.getFluid().getFluidType().getViscosity(fluidStack)\n            )).build());\n\n    /**\n     * If the fluidstack is gaseous\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).symbolOperator(\"lighter_than_air\").interactName(\"isLighterThanAir\")\n            .function(OperatorBuilders.FUNCTION_FLUIDSTACK_TO_BOOLEAN.build(\n                fluidStack -> fluidStack.getFluid().getFluidType().isLighterThanAir()\n            )).build());\n\n    /**\n     * The rarity of the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_RARITY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).symbolOperatorInteract(\"rarity\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                FluidStack a = valueFluidStack.getRawValue();\n                return ValueTypeString.ValueString.of(a.getFluid().getFluidType().getRarity(a).name());\n            }).build());\n\n    /**\n     * The bucket empty sound of the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).symbolOperator(\"sound_bucket_empty\").interactName(\"bucketEmptySound\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                FluidStack a = valueFluidStack.getRawValue();\n                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_EMPTY))\n                        .map(soundEvent -> soundEvent.location().toString())\n                        .orElse(\"\"));\n            }).build());\n\n    /**\n     * The fluid vaporize sound of the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).symbolOperator(\"sound_fluid_vaporize\").interactName(\"fluidVaporizeSound\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                FluidStack a = valueFluidStack.getRawValue();\n                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.FLUID_VAPORIZE))\n                        .map(soundEvent -> soundEvent.location().toString())\n                        .orElse(\"\"));\n            }).build());\n\n    /**\n     * The bucket fill sound of the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).symbolOperator(\"sound_bucket_fill\").interactName(\"bucketFillSound\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                FluidStack a = valueFluidStack.getRawValue();\n                return ValueTypeString.ValueString.of(Optional.ofNullable(a.getFluid().getFluidType().getSound(a, SoundActions.BUCKET_FILL))\n                        .map(soundEvent -> soundEvent.location().toString())\n                        .orElse(\"\"));\n            }).build());\n\n    /**\n     * The bucket of the fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_BUCKET = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.OBJECT_ITEMSTACK).symbolOperatorInteract(\"bucket\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                FluidStack a = valueFluidStack.getRawValue();\n                return ValueObjectTypeItemStack.ValueItemStack.of(a.getFluid().getFluidType().getBucket(a));\n            }).build());\n\n    /**\n     * If the fluid types of the two given fluidstacks are equal, ignoring quantity\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2\n            .output(ValueTypes.BOOLEAN).symbol(\"=Raw=\").operatorName(\"israwfluidequal\").interactName(\"isRawEqual\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack0 = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack1 = variables.getValue(1, ValueTypes.OBJECT_FLUIDSTACK);\n                return ValueTypeBoolean.ValueBoolean.of(FluidStack.isSameFluid(valueFluidStack0.getRawValue(), valueFluidStack1.getRawValue()));\n            }).build());\n\n    /**\n     * The name of the mod owning this fluid\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_MODNAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG.output(ValueTypes.STRING)\n            .symbolOperatorInteract(\"mod\")\n            .function(new IterativeFunction(Lists.newArrayList(\n                    (OperatorBase.SafeVariablesGetter variables) -> {\n                        ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                        return BuiltInRegistries.FLUID.getKey(a.getRawValue().getFluid());\n                    },\n                    OperatorBuilders.PROPAGATOR_RESOURCELOCATION_MODNAME\n            ))).build());\n\n    /**\n     * The tag of the given fluidstack.\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.NBT).symbol(\"NBT()\").operatorInteract(\"nbt\")\n            .function(input -> {\n                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                if (fluidStack.getRawValue().getComponentsPatch().isEmpty()) {\n                    return ValueTypeNbt.ValueNbt.of(Optional.empty());\n                }\n                return ValueTypeNbt.ValueNbt.of(DataComponentPatch.CODEC.encodeStart(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), fluidStack.getRawValue().getComponentsPatch()).getOrThrow());\n            }).build());\n\n    /**\n     * Create a new fluidstack with the given amount.\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_WITH_AMOUNT = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2\n            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.INTEGER)\n            .output(ValueTypes.OBJECT_FLUIDSTACK).symbolOperator(\"with_amount\").interactName(\"withAmount\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack valueFluidStack = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                ValueTypeInteger.ValueInteger valueInteger = variables.getValue(1, ValueTypes.INTEGER);\n                FluidStack fluidStack = valueFluidStack.getRawValue().copy();\n                fluidStack.setAmount(valueInteger.getRawValue());\n                return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);\n            }).build());\n\n    /**\n     * Get the data component keys of an fluidstack.\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_DATA_KEYS = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).symbol(\"data_keys\").operatorName(\"datakeys\").interactName(\"dataKeys\")\n            .function(input -> {\n                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                // Explicitly check for fluid emptiness first, because vanilla sometimes persists NBT when setting stacks to empty\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING,\n                        fluidStack.getRawValue().isEmpty() ?\n                                Lists.newArrayList() :\n                                fluidStack.getRawValue().getComponents().keySet().stream()\n                                        .filter(c -> !c.isTransient())\n                                        .map(c -> ValueTypeString.ValueString.of(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(c).toString()))\n                                        .sorted((o1, o2) -> o1.getRawValue().compareTo(o2.getRawValue()))\n                                        .toList());\n            }).build());\n\n    /**\n     * Get the data component value by key\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_DATA_VALUE = REGISTRY.register(OperatorBuilders.FLUIDSTACK_2_LONG\n            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING)\n            .output(ValueTypes.NBT).symbol(\"data_value\").operatorName(\"datavalue\").interactName(\"dataValue\")\n            .function(input -> {\n                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n\n                // Determine data component type\n                DataComponentType<?> dataComponentType;\n                try {\n                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));\n                } catch (IdentifierException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n\n                // Fetch component value\n                TypedDataComponent<?> typedComponent = fluidStack.getRawValue().getComponents().getTyped(dataComponentType);\n                if (typedComponent == null) {\n                    return ValueTypeNbt.ValueNbt.of((Tag) null);\n                }\n\n                // Encode component value\n                try {\n                    Tag tag = typedComponent.encodeValue(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE)).getOrThrow();\n                    return ValueTypeNbt.ValueNbt.of(tag);\n                } catch (IllegalStateException e) {\n                    return ValueTypeNbt.ValueNbt.of((Tag) null);\n                }\n            }).build());\n\n    /**\n     * Set the data component value by key\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_WITH_DATA = REGISTRY.register(OperatorBuilders.FLUIDSTACK_3\n            .inputTypes(ValueTypes.OBJECT_FLUIDSTACK, ValueTypes.STRING, ValueTypes.NBT)\n            .output(ValueTypes.NBT).symbol(\"with_data\").operatorName(\"withdata\").interactName(\"withData\")\n            .function(input -> {\n                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = input.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n\n                // Skip further processing if input value is empty\n                Optional<Tag> tagOptional = input.getValue(2, ValueTypes.NBT).getRawValue();\n                if (!tagOptional.isPresent()) {\n                    return fluidStack;\n                }\n\n                // Determine data component type\n                DataComponentType<?> dataComponentType;\n                try {\n                    dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(Identifier.parse(input.getValue(1, ValueTypes.STRING).getRawValue()));\n                } catch (IdentifierException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n\n                // Encode component value\n                try {\n                    Object value = dataComponentType.codec().decode(ServerLifecycleHooks.getCurrentServer().registryAccess().createSerializationContext(NbtOps.INSTANCE), tagOptional.get()).getOrThrow().getFirst();\n                    fluidStack = ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack.getRawValue().copy());\n                    fluidStack.getRawValue().set((DataComponentType) dataComponentType, value);\n                    return fluidStack;\n                } catch (IllegalStateException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n            }).build());\n\n    /**\n     * The tag entries of the given fluidstack\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_TAG = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_SUFFIX_LONG\n            .output(ValueTypes.LIST)\n            .symbol(\"fluid_tag_names\").operatorName(\"tag\").interactName(\"tags\")\n            .function(variables -> {\n                ValueObjectTypeFluidStack.ValueFluidStack a = variables.getValue(0, ValueTypes.OBJECT_FLUIDSTACK);\n                ImmutableList.Builder<ValueTypeString.ValueString> builder = ImmutableList.builder();\n                if(!a.getRawValue().isEmpty()) {\n                    a.getRawValue().getFluid().builtInRegistryHolder().tags()\n                            .forEach(owningTag -> builder.add(ValueTypeString.ValueString\n                                    .of(owningTag.location().toString())));\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.STRING, builder.build());\n            }).build());\n\n    /**\n     * Get a list of fluidstacks that correspond to the given tag key.\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_TAG_STACKS = REGISTRY.register(OperatorBuilders.STRING_1_PREFIX\n            .output(ValueTypes.LIST)\n            .symbol(\"fluid_tag_values\").operatorName(\"fluidtag\").interactName(\"fluidsByTag\")\n            .symbol(\"fluid_tag_values\").operatorName(\"fluidtag\").interactName(\"fluidsByTag\")\n            .inputType(ValueTypes.STRING).renderPattern(IConfigRenderPattern.SUFFIX_1_LONG)\n            .function(variables -> {\n                ValueTypeString.ValueString a = variables.getValue(0, ValueTypes.STRING);\n                ImmutableList.Builder<ValueObjectTypeFluidStack.ValueFluidStack> builder = ImmutableList.builder();\n                if (!StringUtil.isNullOrEmpty(a.getRawValue())) {\n                    try {\n                        Helpers.getFluidTagValues(a.getRawValue())\n                                .map(ValueObjectTypeFluidStack.ValueFluidStack::of)\n                                .forEach(builder::add);\n                    } catch (IdentifierException e) {\n                        throw new EvaluationException(Component.translatable(e.getMessage()));\n                    }\n                }\n                return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, builder.build());\n            }).build());\n\n    /**\n     * Get an fluid by name.\n     */\n    public static final IOperator OBJECT_FLUIDSTACK_BY_NAME = REGISTRY.register(OperatorBuilders.FLUIDSTACK_1_PREFIX_LONG\n            .inputType(ValueTypes.STRING).output(ValueTypes.OBJECT_FLUIDSTACK)\n            .symbol(\"fluid_by_name\").operatorName(\"fluidbyname\").interactName(\"fluidByName\")\n            .function(OperatorBuilders.FUNCTION_STRING_TO_RESOURCE_LOCATION\n                    .build(input -> {\n                        Fluid fluid = BuiltInRegistries.FLUID.getValue(input);\n                        FluidStack fluidStack = FluidStack.EMPTY;\n                        if (fluid != null) {\n                            fluidStack = new FluidStack(fluid, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());\n                        }\n                        return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);\n                    })).build());\n\n    /**\n     * ----------------------------------- OPERATOR OPERATORS -----------------------------------\n     */\n\n    /**\n     * Apply for a given operator a given value.\n     */\n    public static final IOperator OPERATOR_APPLY = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"apply\")\n            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY))\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(\n                    input -> {\n                        IOperator innerOperator = input.getLeft();\n                        OperatorBase.SafeVariablesGetter variables = input.getRight();\n                        IVariable variable = variables.getVariables()[0];\n                        return ValueHelpers.evaluateOperator(innerOperator, variable);\n                    })).build());\n    static {\n        REGISTRY.registerSerializer(new CurriedOperator.Serializer());\n    }\n\n    /**\n     * Apply for a given operator the given 2 values.\n     */\n    public static final IOperator OPERATOR_APPLY_2 = REGISTRY.register(OperatorBuilders.OPERATOR\n            .renderPattern(IConfigRenderPattern.INFIX_2)\n            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)\n            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"apply2\")\n            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(\n                    input -> {\n                        IOperator innerOperator = input.getLeft();\n                        OperatorBase.SafeVariablesGetter variables = input.getRight();\n                        IVariable variable0 = variables.getVariables()[0];\n                        IVariable variable1 = variables.getVariables()[1];\n                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1);\n                    })).build());\n\n    /**\n     * Apply for a given operator the given 3 values.\n     */\n    public static final IOperator OPERATOR_APPLY_3 = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .renderPattern(IConfigRenderPattern.INFIX_3)\n            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)\n            .inputTypes(ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"apply3\")\n            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY))\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(\n                    input -> {\n                        IOperator innerOperator = input.getLeft();\n                        OperatorBase.SafeVariablesGetter variables = input.getRight();\n                        IVariable variable0 = variables.getVariables()[0];\n                        IVariable variable1 = variables.getVariables()[1];\n                        IVariable variable2 = variables.getVariables()[2];\n                        return ValueHelpers.evaluateOperator(innerOperator, variable0, variable1, variable2);\n                    })).build());\n\n    /**\n     * Apply for a given operator the given list of values.\n     */\n    public static final IOperator OPERATOR_APPLY_N = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER_LIST)\n            .inputTypes(ValueTypes.OPERATOR, ValueTypes.LIST)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"apply_n\")\n            .typeValidator(OperatorBuilders.createOperatorTypeValidator(ValueTypes.LIST))\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(\n                    input -> {\n                        IOperator innerOperator = input.getLeft();\n                        OperatorBase.SafeVariablesGetter variables = input.getRight();\n                        IValueTypeListProxy<IValueType<IValue>, IValue> list = variables.getValue(0, ValueTypes.LIST).getRawValue();\n                        return ValueHelpers.evaluateOperator(innerOperator, Iterables.toArray(list, IValue.class));\n                    })).build());\n\n    /**\n     * Apply for a given operator with zero arguments.\n     */\n    public static final IOperator OPERATOR_APPLY_0 = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG\n            .conditionalOutputTypeDeriver(OperatorBuilders.OPERATOR_CONDITIONAL_OUTPUT_DERIVER)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"apply0\")\n            .typeValidator(OperatorBuilders.createOperatorTypeValidator(new IValueType[0]))\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR.build(\n                    input -> {\n                        IOperator innerOperator = input.getLeft();\n                        return ValueHelpers.evaluateOperator(innerOperator, new IVariable[0]);\n                    })).build());\n\n    /**\n     * Apply the given operator on all elements of a list, resulting in a new list of mapped values.\n     */\n    public static final IOperator OPERATOR_MAP = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})\n            .output(ValueTypes.LIST).symbolOperatorInteract(\"map\")\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(\n                    input -> {\n                        final IOperator innerOperator = input.getLeft();\n                        OperatorBase.SafeVariablesGetter variables = input.getRight();\n                        ValueTypeList.ValueList inputList = variables.getValue(0, ValueTypes.LIST);\n                        return ValueTypeList.ValueList.ofFactory(\n                                new ValueTypeListProxyOperatorMapped(innerOperator, inputList.getRawValue()));\n                    })).build());\n\n    /**\n     * Filter a list of elements by matching them all with the given predicate.\n     */\n    public static final IOperator OPERATOR_FILTER = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})\n            .output(ValueTypes.LIST).symbolOperatorInteract(\"filter\")\n            .function(OperatorBuilders.FUNCTION_OPERATOR_TAKE_OPERATOR_LIST.build(\n                    new IOperatorValuePropagator<Pair<IOperator, OperatorBase.SafeVariablesGetter>, IValue>() {\n                        @Override\n                        public IValue getOutput(Pair<IOperator, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {\n                            final IOperator innerOperator = input.getLeft();\n                            OperatorBase.SafeVariablesGetter variables = input.getRight();\n                            ValueTypeList.ValueList<?, ?> inputList = variables.getValue(0, ValueTypes.LIST);\n                            if (inputList.getRawValue().isInfinite()) {\n                                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                        OPERATOR_FILTER.getLocalizedNameFull()));\n                            }\n                            List<IValue> filtered = Lists.newArrayList();\n                            for (IValue value : inputList.getRawValue()) {\n                                IValue result = ValueHelpers.evaluateOperator(innerOperator, value);\n                                ValueHelpers.validatePredicateOutput(innerOperator, result);\n                                if (((ValueTypeBoolean.ValueBoolean) result).getRawValue()) {\n                                    filtered.add(value);\n                                }\n                            }\n                            IValueType valueType = inputList.getRawValue().getValueType();\n                            return ValueTypeList.ValueList.ofList(valueType, filtered);\n                        }\n                    })).build());\n\n    /**\n     * Takes the conjunction of two predicates.\n     */\n    public static final IOperator OPERATOR_CONJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})\n            .output(ValueTypes.OPERATOR).symbol(\".&&.\").operatorInteract(\"conjunction\")\n            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(\n                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Conjunction.asOperator(input.getLeft(), input.getRight()))\n            )).build());\n    static {\n        REGISTRY.registerSerializer(new CombinedOperator.Conjunction.Serializer());\n    }\n\n    /**\n     * Takes the disjunction of two predicates.\n     */\n    public static final IOperator OPERATOR_DISJUNCTION = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})\n            .output(ValueTypes.OPERATOR).symbol(\".||.\").operatorInteract(\"disjunction\")\n            .function(OperatorBuilders.FUNCTION_TWO_PREDICATES.build(\n                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Disjunction.asOperator(input.getLeft(), input.getRight()))\n            )).build());\n    static {\n        REGISTRY.registerSerializer(new CombinedOperator.Disjunction.Serializer());\n    }\n\n    /**\n     * Takes the negation of a predicate.\n     */\n    public static final IOperator OPERATOR_NEGATION = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG\n            .renderPattern(IConfigRenderPattern.PREFIX_1)\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})\n            .output(ValueTypes.OPERATOR).symbol(\"!.\").operatorInteract(\"negation\")\n            .function(OperatorBuilders.FUNCTION_ONE_PREDICATE.build(\n                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Negation.asOperator(input))\n            )).build());\n    static {\n        REGISTRY.registerSerializer(new CombinedOperator.Negation.Serializer());\n    }\n\n    /**\n     * Create a new operator that pipes the output from the first operator to the second operator.\n     */\n    public static final IOperator OPERATOR_PIPE = REGISTRY.register(OperatorBuilders.OPERATOR_2_INFIX_LONG\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR})\n            .output(ValueTypes.OPERATOR).symbol(\".\").operatorInteract(\"pipe\")\n            .function(OperatorBuilders.FUNCTION_TWO_OPERATORS.build(\n                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe.asOperator(input.getLeft(), input.getRight()))\n            )).build());\n    static {\n        REGISTRY.registerSerializer(new CombinedOperator.Pipe.Serializer());\n    }\n\n    /**\n     * Create a new operator that gives its input to the first and second operators, and pipes the outputs from both of them to the third operator.\n     */\n    public static final IOperator OPERATOR_PIPE2 = REGISTRY.register(OperatorBuilders.OPERATOR\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR, ValueTypes.OPERATOR})\n            .renderPattern(IConfigRenderPattern.INFIX_2_LATE)\n            .output(ValueTypes.OPERATOR).symbol(\".2\").operatorInteract(\"pipe2\")\n            .function(OperatorBuilders.FUNCTION_THREE_OPERATORS.build(\n                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Pipe2.asOperator(input.getLeft(), input.getMiddle(), input.getRight()))\n            )).build());\n    static {\n        REGISTRY.registerSerializer(new CombinedOperator.Pipe2.Serializer());\n    }\n\n    /**\n     * Flip the input parameters of an operator with two inputs.\n     */\n    public static final IOperator OPERATOR_FLIP = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG\n            .renderPattern(IConfigRenderPattern.PREFIX_1)\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR})\n            .output(ValueTypes.OPERATOR).symbolOperatorInteract(\"flip\")\n            .function(OperatorBuilders.FUNCTION_ONE_OPERATOR.build(\n                input -> ValueTypeOperator.ValueOperator.of(CombinedOperator.Flip.asOperator(input))\n            )).build());\n    static {\n        REGISTRY.registerSerializer(new CombinedOperator.Flip.Serializer());\n    }\n\n    /**\n     * Apply the given operator on all elements of a list to reduce the list to one value.\n     */\n    public static final IOperator OPERATOR_REDUCE = REGISTRY.register(OperatorBuilders.OPERATOR\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.CATEGORY_ANY})\n            .renderPattern(IConfigRenderPattern.PREFIX_3_LONG)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"reduce\")\n            .conditionalOutputTypeDeriver((operator, input) -> input[2].getType())\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    IValue accumulator = variables.getValue(2);\n                    final IOperator innerOperator = OperatorBuilders.getSafeOperator(\n                            variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());\n                    ValueTypeList.ValueList<IValueType<IValue>, IValue> inputList = variables.getValue(1, ValueTypes.LIST);\n                    if (inputList.getRawValue().isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                OPERATOR_REDUCE.getLocalizedNameFull()));\n                    }\n                    for (IValue listValue : inputList.getRawValue()) {\n                        accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);\n                    }\n                    return accumulator;\n                }\n            }).build());\n\n    /**\n     * Apply the given operator on all elements of a list to reduce the list to one value.\n     */\n    public static final IOperator OPERATOR_REDUCE1 = REGISTRY.register(OperatorBuilders.OPERATOR\n            .inputTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST})\n            .renderPattern(IConfigRenderPattern.PREFIX_2_LONG)\n            .output(ValueTypes.CATEGORY_ANY).symbolOperatorInteract(\"reduce1\")\n            .conditionalOutputTypeDeriver((operator, input) -> {\n                try {\n                    IValueTypeListProxy a = ((ValueTypeList.ValueList) input[1].getValue()).getRawValue();\n                    return a.getValueType();\n                } catch (EvaluationException e) {\n                    return operator.getOutputType();\n                }\n            })\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList valueList = variables.getValue(1, ValueTypes.LIST);\n                    if (valueList.getRawValue().isInfinite()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_INFINITELIST_ILLEGAL,\n                                OPERATOR_REDUCE1.getLocalizedNameFull()));\n                    }\n                    Iterator<IValue> iter = valueList.getRawValue().iterator();\n                    if (!iter.hasNext()) {\n                        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_REDUCE_EMPTY));\n                    }\n\n                    IValue accumulator = iter.next();\n                    final IOperator innerOperator = OperatorBuilders.getSafeOperator(\n                            variables.getValue(0, ValueTypes.OPERATOR), accumulator.getType());\n\n                    while (iter.hasNext()) {\n                        IValue listValue = iter.next();\n                        accumulator = ValueHelpers.evaluateOperator(innerOperator, accumulator, listValue);\n                    }\n                    return accumulator;\n                }\n            }).build());\n\n    /**\n     * Apply for a given operator a given value.\n     */\n    public static final IOperator OPERATOR_BY_NAME = REGISTRY.register(OperatorBuilders.OPERATOR_1_PREFIX_LONG\n            .inputType(ValueTypes.STRING).output(ValueTypes.OPERATOR)\n            .symbol(\"op_by_name\").operatorName(\"by_name\").interactName(\"operatorByName\")\n            .function(input -> {\n                ValueTypeString.ValueString name = input.getValue(0, ValueTypes.STRING);\n                try {\n                    Identifier id = Identifier.parse(name.getRawValue());\n                    IOperator operator = Operators.REGISTRY.getOperator(id);\n                    if (operator == null) {\n                        throw new EvaluationException(Component.translatable(\n                                L10NValues.OPERATOR_ERROR_OPERATORNOTFOUND, name.getRawValue()));\n                    }\n                    return ValueTypeOperator.ValueOperator.of(operator);\n                } catch (IdentifierException e) {\n                    throw new EvaluationException(Component.literal(e.getMessage()));\n                }\n            }).build());\n\n    /**\n     * ----------------------------------- NBT OPERATORS -----------------------------------\n     */\n\n    /**\n     * The number of entries in an NBT tag\n     */\n    public static final IOperator NBT_COMPOUND_SIZE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).operatorName(\"compound_size\").symbol(\"NBT{}.size\").interactName(\"size\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_TO_INT.build(\n                opt -> opt.map(CompoundTag::size).orElse(0)\n            )).build());\n\n    /**\n     * The list of keys in an NBT tag\n     */\n    public static final IOperator NBT_COMPOUND_KEYS = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).operatorName(\"compound_keys\").symbol(\"NBT{}.keys\").interactName(\"keys\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtKeys(value.getRawValue()));\n            }).build());\n\n    /**\n     * If an NBT tag has the given key\n     */\n    public static final IOperator NBT_COMPOUND_HASKEY = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.BOOLEAN).operatorName(\"compound_haskey\").symbol(\"NBT{}.has_key\").interactName(\"hasKey\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(\n                    Optional::isPresent\n            )).build());\n\n    /**\n     * The NBT value type of an entry\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_TYPE = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.STRING).operatorName(\"compound_type\").symbol(\"NBT{}.type\").interactName(\"type\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(tag -> {\n                if (tag.isPresent()) {\n                    try {\n                        return TagTypes.getType(tag.get().getId()).getName();\n                    } catch (IndexOutOfBoundsException e) {\n\n                    }\n                }\n                return \"null\";\n            })).build());\n\n    /**\n     * The NBT tag value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_TAG = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.NBT).operatorName(\"compound_value_tag\").symbol(\"NBT{}.get_tag\").interactName(\"getTag\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(o -> o)).build());\n\n    /**\n     * The NBT boolean value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.BOOLEAN).operatorName(\"compound_value_boolean\").symbol(\"NBT{}.get_boolean\").interactName(\"getBoolean\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_BOOLEAN.build(\n                    o -> o.flatMap(Tag::asBoolean).orElse(false)\n            )).build());\n\n    /**\n     * The NBT integer value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_INTEGER = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.INTEGER).operatorName(\"compound_value_integer\").symbol(\"NBT{}.get_integer\").interactName(\"getInteger\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_INT.build(\n                    o -> o.flatMap(Tag::asInt).orElse(0)\n            )).build());\n\n    /**\n     * The NBT long value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_LONG = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.LONG).operatorName(\"compound_value_long\").symbol(\"NBT{}.get_long\").interactName(\"getLong\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_LONG.build(\n                    o -> o.flatMap(Tag::asLong).orElse(0L)\n            )).build());\n\n    /**\n     * The NBT double value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.DOUBLE).operatorName(\"compound_value_double\").symbol(\"NBT{}.get_double\").interactName(\"getDouble\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_DOUBLE.build(\n                    o -> o.flatMap(Tag::asDouble).orElse(0D)\n            )).build());\n\n    /**\n     * The NBT string value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_STRING = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.STRING).operatorName(\"compound_value_string\").symbol(\"NBT{}.get_string\").interactName(\"getString\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_STRING.build(\n                    o -> o.flatMap(Tag::asString).orElse(\"\")\n            )).build());\n\n    /**\n     * The NBT compound value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.NBT).operatorName(\"compound_value_compound\").symbol(\"NBT{}.get_compound\").interactName(\"getCompound\")\n            .function(OperatorBuilders.FUNCTION_NBT_COMPOUND_ENTRY_TO_NBT.build(\n                    o -> o.map(tag -> tag instanceof CompoundTag ? (CompoundTag) tag : new CompoundTag())\n            )).build());\n\n    /**\n     * The NBT tag list value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.LIST).operatorName(\"compound_value_list_tag\").symbol(\"NBT{}.get_list_tag\").interactName(\"getListTag\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListTag(key.getRawValue(), value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT boolean list value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.LIST).operatorName(\"compound_value_list_byte\").symbol(\"NBT{}.get_list_byte\").interactName(\"getListByte\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListByte(key.getRawValue(), value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT int list value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.LIST).operatorName(\"compound_value_list_int\").symbol(\"NBT{}.get_list_int\").interactName(\"getListInt\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListInt(key.getRawValue(), value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT long list value\n     */\n    public static final IOperator NBT_COMPOUND_VALUE_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.LIST).operatorName(\"compound_value_list_long\").symbol(\"NBT{}.get_list_long\").interactName(\"getListLong\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeString.ValueString key = variables.getValue(1, ValueTypes.STRING);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtValueListLong(key.getRawValue(), value.getRawValue()));\n            }).build());\n\n    /**\n     * Remove an entry from an NBT compound\n     */\n    public static final IOperator NBT_COMPOUND_WITHOUT = REGISTRY.register(OperatorBuilders.NBT_2\n            .output(ValueTypes.NBT).operatorName(\"compound_without\").symbol(\"NBT{}.without\").interactName(\"without\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt valueNbt = variables.getValue(0, ValueTypes.NBT);\n                Optional<Tag> tag = valueNbt.getRawValue();\n                if (tag.isPresent()) {\n                    if (!(tag.get() instanceof CompoundTag)) {\n                        return ValueTypeNbt.ValueNbt.of();\n                    }\n                    ValueTypeString.ValueString valueString = variables.getValue(1, ValueTypes.STRING);\n                    String key = valueString.getRawValue();\n                    CompoundTag tagCompound = (CompoundTag) tag.get();\n                    if (tagCompound.contains(key)) {\n                        // Copy the tag to ensure immutability\n                        tagCompound = tagCompound.copy();\n                        tagCompound.remove(key);\n                    }\n                    return ValueTypeNbt.ValueNbt.of(tagCompound);\n                }\n                return valueNbt;\n            }).build());\n\n\n\n    /**\n     * Set an NBT compound boolean value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_3\n            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.BOOLEAN)\n            .operatorName(\"compound_with_boolean\").symbol(\"NBT{}.with_boolean\").interactName(\"withBoolean\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeBoolean.ValueBoolean value = input.getRight().getValue(0, ValueTypes.BOOLEAN);\n                input.getLeft().ifPresent(tag -> tag.putBoolean(input.getMiddle(), value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound short value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_SHORT = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)\n            .operatorName(\"compound_with_short\").symbol(\"NBT{}.with_short\").interactName(\"withShort\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);\n                input.getLeft().ifPresent(tag -> tag.putShort(input.getMiddle(), (short) value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound integer value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_INTEGER = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.INTEGER)\n            .operatorName(\"compound_with_integer\").symbol(\"NBT{}.with_integer\").interactName(\"withInteger\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeInteger.ValueInteger value = input.getRight().getValue(0, ValueTypes.INTEGER);\n                input.getLeft().ifPresent(tag -> tag.putInt(input.getMiddle(), value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound long value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_LONG = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LONG)\n            .operatorName(\"compound_with_long\").symbol(\"NBT{}.with_long\").interactName(\"withLong\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeLong.ValueLong value = input.getRight().getValue(0, ValueTypes.LONG);\n                input.getLeft().ifPresent(tag -> tag.putLong(input.getMiddle(), value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound double value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)\n            .operatorName(\"compound_with_double\").symbol(\"NBT{}.with_double\").interactName(\"withDouble\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);\n                input.getLeft().ifPresent(tag -> tag.putDouble(input.getMiddle(), value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound float value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_FLOAT = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.DOUBLE)\n            .operatorName(\"compound_with_float\").symbol(\"NBT{}.with_float\").interactName(\"withFloat\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeDouble.ValueDouble value = input.getRight().getValue(0, ValueTypes.DOUBLE);\n                input.getLeft().ifPresent(tag -> tag.putFloat(input.getMiddle(), (float) value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound string value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_STRING = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.STRING)\n            .operatorName(\"compound_with_string\").symbol(\"NBT{}.with_string\").interactName(\"withString\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeString.ValueString value = input.getRight().getValue(0, ValueTypes.STRING);\n                input.getLeft().ifPresent(tag -> tag.putString(input.getMiddle(), value.getRawValue()));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound compound value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_COMPOUND = REGISTRY.register(OperatorBuilders.NBT_3\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.NBT)\n            .operatorName(\"compound_with_tag\").symbol(\"NBT{}.with_tag\").interactName(\"withTag\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(input -> {\n                ValueTypeNbt.ValueNbt value = input.getRight().getValue(0, ValueTypes.NBT);\n                input.getLeft()\n                        .ifPresent(tag -> value.getRawValue()\n                                .ifPresent(v -> tag.put(input.getMiddle(), v)));\n                return input.getLeft();\n            })).build());\n\n    /**\n     * Set an NBT compound tag list value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_LIST_TAG = REGISTRY.register(OperatorBuilders.NBT_3\n            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)\n            .operatorName(\"compound_with_list_tag\").symbol(\"NBT{}.with_tag_list\").interactName(\"withTagList\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {\n                @Override\n                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {\n                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);\n                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),\n                            NbtHelpers.getListNbtTag(value, NBT_COMPOUND_WITH_LIST_TAG.getLocalizedNameFull())));\n                    return input.getLeft();\n                }\n            })).build());\n\n    /**\n     * Set an NBT compound byte list value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_LIST_BYTE = REGISTRY.register(OperatorBuilders.NBT_3\n            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)\n            .operatorName(\"compound_with_list_byte\").symbol(\"NBT{}.with_byte_list\").interactName(\"withByteList\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {\n                @Override\n                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {\n                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);\n                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),\n                            NbtHelpers.getListNbtByte(value, NBT_COMPOUND_WITH_LIST_BYTE.getLocalizedNameFull())));\n                    return input.getLeft();\n                }\n            })).build());\n\n    /**\n     * Set an NBT compound int list value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_LIST_INT = REGISTRY.register(OperatorBuilders.NBT_3\n            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)\n            .operatorName(\"compound_with_list_int\").symbol(\"NBT{}.with_int_list\").interactName(\"withIntList\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {\n                @Override\n                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {\n                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);\n                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),\n                            NbtHelpers.getListNbtInt(value, NBT_COMPOUND_WITH_LIST_INT.getLocalizedNameFull())));\n                    return input.getLeft();\n                }\n            })).build());\n\n    /**\n     * Set an NBT compound long list value\n     */\n    public static final IOperator NBT_COMPOUND_WITH_LIST_LONG = REGISTRY.register(OperatorBuilders.NBT_3\n            .renderPattern(IConfigRenderPattern.INFIX_2_VERYLONG)\n            .inputTypes(ValueTypes.NBT, ValueTypes.STRING, ValueTypes.LIST)\n            .operatorName(\"compound_with_list_long\").symbol(\"NBT{}.with_list_long\").interactName(\"withListLong\")\n            .function(OperatorBuilders.FUNCTION_NBT_COPY_FOR_VALUE_TO_NBT.build(new IOperatorValuePropagator<Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter>, Optional<CompoundTag>>() {\n                @Override\n                public Optional<CompoundTag> getOutput(Triple<Optional<CompoundTag>, String, OperatorBase.SafeVariablesGetter> input) throws EvaluationException {\n                    ValueTypeList.ValueList<?, ?> value = input.getRight().getValue(0, ValueTypes.LIST);\n                    input.getLeft().ifPresent(tag -> tag.put(input.getMiddle(),\n                            NbtHelpers.getListNbtLong(value, NBT_COMPOUND_WITH_LIST_LONG.getLocalizedNameFull())));\n                    return input.getLeft();\n                }\n            })).build());\n\n    /**\n     * Check if the first NBT compound tag is a subset of the second NBT compound tag.\n     */\n    public static final IOperator NBT_COMPOUND_SUBSET = REGISTRY.register(OperatorBuilders.NBT_2_NBT\n            .output(ValueTypes.BOOLEAN).operatorName(\"compound_subset\").symbol(\"NBT{}.⊆\").interactName(\"isSubset\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);\n                if (valueNbt0.getRawValue().isPresent()\n                        && valueNbt1.getRawValue().isPresent()\n                        && valueNbt0.getRawValue().get() instanceof CompoundTag\n                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {\n                    return ValueTypeBoolean.ValueBoolean.of(NbtHelpers.nbtMatchesSubset((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get(), true));\n                }\n                return ValueTypeBoolean.ValueBoolean.of(false);\n            }).build());\n\n    /**\n     * The union of the given NBT compound tags. Nested tags will be joined recusively.\n     */\n    public static final IOperator NBT_COMPOUND_UNION = REGISTRY.register(OperatorBuilders.NBT_2_NBT\n            .output(ValueTypes.NBT).operatorName(\"compound_union\").symbol(\"NBT{}.∪\").interactName(\"union\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);\n                if (valueNbt0.getRawValue().isPresent()\n                        && valueNbt1.getRawValue().isPresent()\n                        && valueNbt0.getRawValue().get() instanceof CompoundTag\n                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.union((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));\n                }\n                return ValueTypeNbt.ValueNbt.of();\n            }).build());\n\n    /**\n     * The intersection of the given NBT compound tags. Nested tags will be intersected recusively.\n     */\n    public static final IOperator NBT_COMPOUND_INTERSECTION = REGISTRY.register(OperatorBuilders.NBT_2_NBT\n            .output(ValueTypes.NBT).operatorName(\"compound_intersection\").symbol(\"NBT{}.∩\").interactName(\"intersection\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);\n                if (valueNbt0.getRawValue().isPresent()\n                        && valueNbt1.getRawValue().isPresent()\n                        && valueNbt0.getRawValue().get() instanceof CompoundTag\n                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.intersection((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));\n                }\n                return ValueTypeNbt.ValueNbt.of();\n            }).build());\n\n    /**\n     * The difference of the given NBT compound tags. Nested tags will be subtracted recusively.\n     */\n    public static final IOperator NBT_COMPOUND_MINUS = REGISTRY.register(OperatorBuilders.NBT_2_NBT\n            .output(ValueTypes.NBT).operatorName(\"compound_minus\").symbol(\"NBT{}.∖\").interactName(\"minus\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt valueNbt0 = variables.getValue(0, ValueTypes.NBT);\n                ValueTypeNbt.ValueNbt valueNbt1 = variables.getValue(1, ValueTypes.NBT);\n                if (valueNbt0.getRawValue().isPresent()\n                        && valueNbt1.getRawValue().isPresent()\n                        && valueNbt0.getRawValue().get() instanceof CompoundTag\n                        && valueNbt1.getRawValue().get() instanceof CompoundTag) {\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.minus((CompoundTag) valueNbt0.getRawValue().get(), (CompoundTag) valueNbt1.getRawValue().get()));\n                }\n                return ValueTypeNbt.ValueNbt.of();\n            }).build());\n\n    /**\n     * The boolean value of an NBT value\n     */\n    public static final IOperator NBT_AS_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.BOOLEAN).operatorName(\"as_boolean\").symbol(\"NBT.as_boolean\").interactName(\"asBoolean\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_BOOLEAN.build(\n                    o -> o.flatMap(Tag::asBoolean).orElse(false)\n            )).build());\n\n    /**\n     * The byte value of an NBT value\n     */\n    public static final IOperator NBT_AS_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).operatorName(\"as_byte\").symbol(\"NBT.as_byte\").interactName(\"asByte\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(\n                    o -> o.flatMap(Tag::asByte).map(s -> (int) s).orElse(0)\n            )).build());\n\n    /**\n     * The short value of an NBT value\n     */\n    public static final IOperator NBT_AS_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).operatorName(\"as_short\").symbol(\"NBT.as_short\").interactName(\"asShort\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(\n                    o -> o.flatMap(Tag::asShort).map(s -> (int) s).orElse(0)\n            )).build());\n\n    /**\n     * The int value of an NBT value\n     */\n    public static final IOperator NBT_AS_INT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.INTEGER).operatorName(\"as_int\").symbol(\"NBT.as_int\").interactName(\"asInt\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_INT.build(\n                    o -> o.flatMap(Tag::asInt).orElse(0)\n            )).build());\n\n    /**\n     * The long value of an NBT value\n     */\n    public static final IOperator NBT_AS_LONG = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.LONG).operatorName(\"as_long\").symbol(\"NBT.as_long\").interactName(\"asLong\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_LONG.build(\n                    o -> o.flatMap(Tag::asLong).orElse(0L)\n            )).build());\n\n    /**\n     * The double value of an NBT value\n     */\n    public static final IOperator NBT_AS_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.DOUBLE).operatorName(\"as_double\").symbol(\"NBT.as_double\").interactName(\"asDouble\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(\n                    o -> o.flatMap(Tag::asDouble).orElse(0D)\n            )).build());\n\n    /**\n     * The float value of an NBT value\n     */\n    public static final IOperator NBT_AS_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.DOUBLE).operatorName(\"as_float\").symbol(\"NBT.as_float\").interactName(\"asFloat\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_DOUBLE.build(\n                    o -> o.flatMap(Tag::asFloat).map(f -> (double) f).orElse(0D)\n            )).build());\n\n    /**\n     * The string value of an NBT value\n     */\n    public static final IOperator NBT_AS_STRING = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.STRING).operatorName(\"as_string\").symbol(\"NBT.as_string\").interactName(\"asString\")\n            .function(OperatorBuilders.FUNCTION_NBT_TO_STRING.build(\n                    o -> o.flatMap(Tag::asString).orElse(\"\")\n            )).build());\n\n    /**\n     * The tag list value of an NBT value\n     */\n    public static final IOperator NBT_AS_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).operatorName(\"as_tag_list\").symbol(\"NBT.as_tag_list\").interactName(\"asTagList\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListTag(value.getRawValue()));\n            }).build());\n\n    /**\n     * The byte list value of an NBT value\n     */\n    public static final IOperator NBT_AS_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).operatorName(\"as_byte_list\").symbol(\"NBT.as_byte_list\").interactName(\"asByteList\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListByte(value.getRawValue()));\n            }).build());\n\n    /**\n     * The int list value of an NBT value\n     */\n    public static final IOperator NBT_AS_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).operatorName(\"as_int_list\").symbol(\"NBT.as_int_list\").interactName(\"asIntList\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListInt(value.getRawValue()));\n            }).build());\n\n    /**\n     * The long list value of an NBT value\n     */\n    public static final IOperator NBT_AS_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_SUFFIX_LONG\n            .output(ValueTypes.LIST).operatorName(\"as_long_list\").symbol(\"NBT.as_long_list\").interactName(\"asLongList\")\n            .function(variables -> {\n                ValueTypeNbt.ValueNbt value = variables.getValue(0, ValueTypes.NBT);\n                return ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyNbtAsListLong(value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a boolean value\n     */\n    public static final IOperator NBT_FROM_BOOLEAN = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.BOOLEAN).output(ValueTypes.NBT)\n            .operatorName(\"from_boolean\").symbol(\"NBT.from_boolean\").interactName(\"asNbt\")\n            .function(variables -> {\n                ValueTypeBoolean.ValueBoolean value = variables.getValue(0, ValueTypes.BOOLEAN);\n                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf(value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a short value\n     */\n    public static final IOperator NBT_FROM_SHORT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)\n            .operatorName(\"from_short\").symbol(\"NBT.from_short\").interactName(\"asNbt\", \"short\", true)\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);\n                return ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a byte value\n     */\n    public static final IOperator NBT_FROM_BYTE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)\n            .operatorName(\"from_byte\").symbol(\"NBT.from_byte\").interactName(\"asNbt\", \"byte\", true)\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);\n                return ValueTypeNbt.ValueNbt.of(ByteTag.valueOf((byte) value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of an int value\n     */\n    public static final IOperator NBT_FROM_INT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.INTEGER).output(ValueTypes.NBT)\n            .operatorName(\"from_int\").symbol(\"NBT.from_int\").interactName(\"asNbt\")\n            .function(variables -> {\n                ValueTypeInteger.ValueInteger value = variables.getValue(0, ValueTypes.INTEGER);\n                return ValueTypeNbt.ValueNbt.of(IntTag.valueOf(value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a long value\n     */\n    public static final IOperator NBT_FROM_LONG = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.LONG).output(ValueTypes.NBT)\n            .operatorName(\"from_long\").symbol(\"NBT.from_long\").interactName(\"asNbt\")\n            .function(variables -> {\n                ValueTypeLong.ValueLong value = variables.getValue(0, ValueTypes.LONG);\n                return ValueTypeNbt.ValueNbt.of(LongTag.valueOf(value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a double value\n     */\n    public static final IOperator NBT_FROM_DOUBLE = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)\n            .operatorName(\"from_double\").symbol(\"NBT.from_double\").interactName(\"asNbt\")\n            .function(variables -> {\n                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);\n                return ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a float value\n     */\n    public static final IOperator NBT_FROM_FLOAT = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.DOUBLE).output(ValueTypes.NBT)\n            .operatorName(\"from_float\").symbol(\"NBT.from_float\").interactName(\"asNbt\", \"float\", true)\n            .function(variables -> {\n                ValueTypeDouble.ValueDouble value = variables.getValue(0, ValueTypes.DOUBLE);\n                return ValueTypeNbt.ValueNbt.of(FloatTag.valueOf((float) value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a string value\n     */\n    public static final IOperator NBT_FROM_STRING = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.STRING).output(ValueTypes.NBT)\n            .operatorName(\"from_string\").symbol(\"NBT.from_string\").interactName(\"asNbt\")\n            .function(variables -> {\n                ValueTypeString.ValueString value = variables.getValue(0, ValueTypes.STRING);\n                return ValueTypeNbt.ValueNbt.of(StringTag.valueOf(value.getRawValue()));\n            }).build());\n\n    /**\n     * The NBT value of a tag list value\n     */\n    public static final IOperator NBT_FROM_TAG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)\n            .operatorName(\"from_tag_list\").symbol(\"NBT.from_tag_list\").interactName(\"asNbt\", \"tagList\", true)\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtTag(value, NBT_FROM_TAG_LIST.getLocalizedNameFull()));\n                }\n            }).build());\n\n    /**\n     * The NBT value of a byte list value\n     */\n    public static final IOperator NBT_FROM_BYTE_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)\n            .operatorName(\"from_byte_list\").symbol(\"NBT.from_byte_list\").interactName(\"asNbt\", \"byteList\", true)\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtByte(value, NBT_FROM_BYTE_LIST.getLocalizedNameFull()));\n                }\n            }).build());\n\n    /**\n     * The NBT value of a int list value\n     */\n    public static final IOperator NBT_FROM_INT_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)\n            .operatorName(\"from_int_list\").symbol(\"NBT.from_int_list\").interactName(\"asNbt\", \"intList\", true)\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtInt(value, NBT_FROM_INT_LIST.getLocalizedNameFull()));\n                }\n            }).build());\n\n    /**\n     * The NBT value of a long list value\n     */\n    public static final IOperator NBT_FROM_LONG_LIST = REGISTRY.register(OperatorBuilders.NBT_1_PREFIX_LONG\n            .inputType(ValueTypes.LIST).output(ValueTypes.NBT)\n            .operatorName(\"from_long_list\").symbol(\"NBT.from_long_list\").interactName(\"asNbt\", \"longList\", true)\n            .function(new OperatorBase.IFunction() {\n                @Override\n                public IValue evaluate(OperatorBase.SafeVariablesGetter variables) throws EvaluationException {\n                    ValueTypeList.ValueList value = variables.getValue(0, ValueTypes.LIST);\n                    return ValueTypeNbt.ValueNbt.of(NbtHelpers.getListNbtLong(value, NBT_FROM_LONG_LIST.getLocalizedNameFull()));\n                }\n            }).build());\n\n    /**\n     * Apply the given NBT path expression on the given NBT value and get the first result.\n     */\n    public static final IOperator NBT_PATH_MATCH_FIRST = REGISTRY.register(OperatorBuilders.NBT_2\n            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.NBT)\n            .operatorName(\"path_match_first\").symbol(\"NBT.path_match_first\").interactName(\"nbtPathMatchFirst\")\n            .function(variables -> {\n                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);\n                INbtPathExpression expression = null;\n                try {\n                    expression = NbtPath.parse(string.getRawValue());\n                } catch (NbtParseException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,\n                            string.getRawValue(),\n                            e.getMessage()));\n                }\n                if (!nbt.getRawValue().isPresent()) {\n                    return ValueTypeNbt.ValueNbt.of();\n                }\n                return ValueTypeNbt.ValueNbt.of(expression.match(nbt.getRawValue().get()).getMatches().findAny());\n            }).build());\n\n    /**\n     * Apply the given NBT path expression on the given NBT value and get all results.\n     */\n    public static final IOperator NBT_PATH_MATCH_ALL = REGISTRY.register(OperatorBuilders.NBT_2\n            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.LIST)\n            .operatorName(\"path_match_all\").symbol(\"NBT.path_match_all\").interactName(\"nbtPathMatchAll\")\n            .function(variables -> {\n                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);\n                INbtPathExpression expression = null;\n                try {\n                    expression = NbtPath.parse(string.getRawValue());\n                } catch (NbtParseException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,\n                            string.getRawValue(),\n                            e.getMessage()));\n                }\n                if (!nbt.getRawValue().isPresent()) {\n                    return ValueTypeList.ValueList.ofAll(ValueTypes.NBT);\n                }\n                List<ValueTypeNbt.ValueNbt> matches = expression.match(nbt.getRawValue().get()).getMatches()\n                        .map(ValueTypeNbt.ValueNbt::of)\n                        .collect(Collectors.toList());\n                return ValueTypeList.ValueList.ofList(ValueTypes.NBT, matches);\n            }).build());\n\n    /**\n     * Test the given NBT path expression on the given NBT value.\n     */\n    public static final IOperator NBT_PATH_TEST = REGISTRY.register(OperatorBuilders.NBT_2\n            .inputTypes(ValueTypes.STRING, ValueTypes.NBT).output(ValueTypes.BOOLEAN)\n            .operatorName(\"path_test\").symbol(\"NBT.path_test\").interactName(\"nbtPathTest\")\n            .function(variables -> {\n                ValueTypeString.ValueString string = variables.getValue(0, ValueTypes.STRING);\n                ValueTypeNbt.ValueNbt nbt = variables.getValue(1, ValueTypes.NBT);\n                INbtPathExpression expression = null;\n                try {\n                    expression = NbtPath.parse(string.getRawValue());\n                } catch (NbtParseException e) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_NBT_PATH_EXPRESSION,\n                            string.getRawValue(),\n                            e.getMessage()));\n                }\n                if (!nbt.getRawValue().isPresent()) {\n                    return ValueTypeBoolean.ValueBoolean.of(false);\n                }\n                return ValueTypeBoolean.ValueBoolean.of(expression.test(nbt.getRawValue().get()));\n            }).build());\n\n    /**\n     * ----------------------------------- INGREDIENTS OPERATORS -----------------------------------\n     */\n\n    /**\n     * The list of items.\n     */\n    public static final IOperator INGREDIENTS_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG\n            .output(ValueTypes.LIST).operatorInteract(\"items\").symbol(\"Ingr.items\")\n            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ITEMSTACK))\n            .build());\n\n    /**\n     * The list of fluids\n     */\n    public static final IOperator INGREDIENTS_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG\n            .output(ValueTypes.LIST).operatorInteract(\"fluids\").symbol(\"Ingr.fluids\")\n            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.FLUIDSTACK))\n            .build());\n\n    /**\n     * The list of fluids\n     */\n    public static final IOperator INGREDIENTS_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_1_PREFIX_LONG\n            .output(ValueTypes.LIST).operatorInteract(\"energies\").symbol(\"Ingr.energies\")\n            .function(OperatorBuilders.createFunctionIngredientsList(() -> IngredientComponent.ENERGY))\n            .build());\n\n    /**\n     * Set an ingredient item\n     */\n    public static final IOperator INGREDIENTS_WITH_ITEM = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_ITEMSTACK\n            .operatorName(\"with_item\").symbol(\"Ingr.with_item\").interactName(\"withItem\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);\n                ValueObjectTypeItemStack.ValueItemStack itemStack = variables.getValue(2, ValueTypes.OBJECT_ITEMSTACK);\n                if (value.getRawValue().isEmpty()) {\n                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));\n                }\n                IMixedIngredients baseIngredients = value.getRawValue().get();\n                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,\n                        index.getRawValue(), IngredientComponent.ITEMSTACK, itemStack.getRawValue()));\n            }).build());\n\n    /**\n     * Set an ingredient fluid\n     */\n    public static final IOperator INGREDIENTS_WITH_FLUID = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_FLUIDSTACK\n            .operatorName(\"with_fluid\").symbol(\"Ingr.with_fluid\").interactName(\"withFluid\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);\n                ValueObjectTypeFluidStack.ValueFluidStack fluidStack = variables.getValue(2, ValueTypes.OBJECT_FLUIDSTACK);\n                if (value.getRawValue().isEmpty()) {\n                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));\n                }\n                IMixedIngredients baseIngredients = value.getRawValue().get();\n                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,\n                        index.getRawValue(), IngredientComponent.FLUIDSTACK, fluidStack.getRawValue()));\n            }).build());\n\n    /**\n     * Set an ingredient energy\n     */\n    public static final IOperator INGREDIENTS_WITH_ENERGY = REGISTRY.register(OperatorBuilders.INGREDIENTS_3_LONG\n            .operatorName(\"with_energy\").symbol(\"Ingr.with_energy\").interactName(\"withEnergy\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients value = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueTypeInteger.ValueInteger index = variables.getValue(1, ValueTypes.INTEGER);\n                ValueTypeLong.ValueLong energy = variables.getValue(2, ValueTypes.LONG);\n                if (value.getRawValue().isEmpty()) {\n                    value = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));\n                }\n                IMixedIngredients baseIngredients = value.getRawValue().get();\n                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsSingle<>(baseIngredients,\n                        index.getRawValue(), IngredientComponent.ENERGY, energy.getRawValue()));\n            }).build());\n\n    /**\n     * Set the list of items\n     */\n    public static final IOperator INGREDIENTS_WITH_ITEMS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST\n            .operatorName(\"with_items\").symbol(\"Ingr.with_items\").interactName(\"withItems\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> list = variables.getValue(1, ValueTypes.LIST);\n                if (valueIngredients.getRawValue().isEmpty()) {\n                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));\n                }\n                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();\n                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,\n                        IngredientComponent.ITEMSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ITEMSTACK, list)));\n            }).build());\n\n    /**\n     * Set the list of fluids\n     */\n    public static final IOperator INGREDIENTS_WITH_FLUIDS = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST\n            .operatorName(\"with_fluids\").symbol(\"Ingr.with_fluids\").interactName(\"withFluids\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueTypeList.ValueList<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> list = variables.getValue(1, ValueTypes.LIST);\n                if (valueIngredients.getRawValue().isEmpty()) {\n                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));\n                }\n                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();\n                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,\n                        IngredientComponent.FLUIDSTACK, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.FLUIDSTACK, list)));\n            }).build());\n\n    /**\n     * Set the list of energies\n     */\n    public static final IOperator INGREDIENTS_WITH_ENERGIES = REGISTRY.register(OperatorBuilders.INGREDIENTS_2_LIST\n            .renderPattern(IConfigRenderPattern.INFIX_VERYLONG)\n            .operatorName(\"with_energies\").symbol(\"Ingr.with_energies\").interactName(\"withEnergies\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueTypeList.ValueList<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = variables.getValue(1, ValueTypes.LIST);\n                if (valueIngredients.getRawValue().isEmpty()) {\n                    valueIngredients = ValueObjectTypeIngredients.ValueIngredients.of(new MixedIngredients(Maps.newIdentityHashMap()));\n                }\n                IMixedIngredients baseIngredients = valueIngredients.getRawValue().get();\n                return ValueObjectTypeIngredients.ValueIngredients.of(new ExtendedIngredientsList<>(baseIngredients,\n                        IngredientComponent.ENERGY, OperatorBuilders.unwrapIngredientComponentList(IngredientComponent.ENERGY, list)));\n            }).build());\n\n    /**\n     * ----------------------------------- RECIPE OPERATORS -----------------------------------\n     */\n\n    /**\n     * The input ingredients of a recipe\n     */\n    public static final IOperator RECIPE_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG\n            .output(ValueTypes.OBJECT_INGREDIENTS)\n            .operatorInteract(\"input\").symbol(\"recipe_in\")\n            .function(variables -> {\n                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);\n                if (value.getRawValue().isPresent()) {\n                    return ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(value.getRawValue().get()));\n                }\n                return ValueObjectTypeIngredients.ValueIngredients.of(null);\n            }).build());\n\n    /**\n     * The output ingredients of a recipe\n     */\n    public static final IOperator RECIPE_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_1_SUFFIX_LONG\n            .output(ValueTypes.OBJECT_INGREDIENTS)\n            .operatorInteract(\"output\").symbol(\"recipe_out\")\n            .function(variables -> {\n                ValueObjectTypeRecipe.ValueRecipe value = variables.getValue(0, ValueTypes.OBJECT_RECIPE);\n                if (value.getRawValue().isPresent()) {\n                    return ValueObjectTypeIngredients.ValueIngredients.of(value.getRawValue().get().getOutput());\n                }\n                return ValueObjectTypeIngredients.ValueIngredients.of(null);\n            }).build());\n\n    /**\n     * Set the input ingredients of a recipe\n     */\n    public static final IOperator RECIPE_WITH_INPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX\n            .output(ValueTypes.OBJECT_RECIPE)\n            .operatorName(\"with_input\").symbol(\"Recipe.with_in\").interactName(\"withInput\")\n            .function(variables -> {\n                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);\n                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);\n                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {\n                    IMixedIngredients ingredients = valueIngredients.getRawValue().get();\n                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();\n                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {\n                        IIngredientMatcher matcher = component.getMatcher();\n                        inputs.put(component, (List) ingredients.getInstances(component)\n                                .stream()\n                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))\n                                .collect(Collectors.toList()));\n                    }\n                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(\n                            inputs,\n                            valueRecipe.getRawValue().get().getOutput()\n                    ));\n                }\n                return ValueObjectTypeRecipe.ValueRecipe.of(null);\n            }).build());\n\n    /**\n     * Set the output ingredients of a recipe\n     */\n    public static final IOperator RECIPE_WITH_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_INFIX\n            .output(ValueTypes.OBJECT_RECIPE)\n            .operatorName(\"with_output\").symbol(\"Recipe.with_out\").interactName(\"withOutput\")\n            .function(variables -> {\n                ValueObjectTypeRecipe.ValueRecipe valueRecipe = variables.getValue(0, ValueTypes.OBJECT_RECIPE);\n                ValueObjectTypeIngredients.ValueIngredients valueIngredients = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);\n                if (valueRecipe.getRawValue().isPresent() && valueIngredients.getRawValue().isPresent()) {\n                    IRecipeDefinition recipe = valueRecipe.getRawValue().get();\n                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();\n                    for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {\n                        inputs.put(component, (List) recipe.getInputs(component));\n                    }\n                    return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(\n                            inputs,\n                            valueIngredients.getRawValue().get()\n                    ));\n                }\n                return ValueObjectTypeRecipe.ValueRecipe.of(null);\n            }).build());\n\n    /**\n     * Create a recipe from two the given I/O ingredients\n     */\n    public static final IOperator RECIPE_WITH_INPUT_OUTPUT = REGISTRY.register(OperatorBuilders.RECIPE_2_PREFIX\n            .output(ValueTypes.OBJECT_RECIPE)\n            .operatorName(\"with_input_output\").symbol(\"Recipe.with_io\").interactName(\"withInputOutput\")\n            .function(variables -> {\n                ValueObjectTypeIngredients.ValueIngredients valueIn = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n                ValueObjectTypeIngredients.ValueIngredients valueOut = variables.getValue(1, ValueTypes.OBJECT_INGREDIENTS);\n                if (valueIn.getRawValue().isPresent() && valueOut.getRawValue().isPresent()) {\n                    IMixedIngredients ingredients = valueIn.getRawValue().get();\n                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();\n                    for (IngredientComponent<?, ?> component : ingredients.getComponents()) {\n                        IIngredientMatcher matcher = component.getMatcher();\n                        inputs.put(component, (List) ingredients.getInstances(component)\n                                .stream()\n                                .map(instance -> new PrototypedIngredientAlternativesList(Collections.singletonList(new PrototypedIngredient(component, instance, matcher.getExactMatchCondition()))))\n                                .collect(Collectors.toList()));\n                    }\n                    try {\n                        return ValueObjectTypeRecipe.ValueRecipe.of(new RecipeDefinition(\n                                inputs,\n                                valueOut.getRawValue().get()\n                        ));\n                    } catch (IllegalArgumentException e) {\n                        throw new EvaluationException(Component.literal(e.getMessage()));\n                    }\n                }\n                return ValueObjectTypeRecipe.ValueRecipe.of(null);\n            }).build());\n\n    /**\n     * ------------------------------------ PARSE OPERATORS ------------------------------------\n     */\n\n    /**\n     * Boolean Parse operator which takes a string of form `/(F(alse)?|[+-]?(0x|#)?0+|)/i`.\n     */\n    public static final IOperator PARSE_BOOLEAN = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.BOOLEAN, v -> {\n      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);\n      Pattern p = Pattern.compile(\"\\\\A(F(alse)?|[+-]?(0x|#)?0+|)\\\\z\", Pattern.CASE_INSENSITIVE);\n      return ValueTypeBoolean.ValueBoolean.of(!p.matcher(value.getRawValue().trim()).matches());\n    }));\n\n    /**\n     * Double Parse operator which takes a string of a form Double.parseDouble(),\n     * `/([+-]?)(Inf(inity)?|\\u221E)/i`, or Long.decode() can consume.\n     */\n    public static final IOperator PARSE_DOUBLE = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.DOUBLE, v -> {\n      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);\n      try {\n        return ValueTypeDouble.ValueDouble.of(Double.parseDouble(value.getRawValue()));\n      } catch (NumberFormatException e) {\n        try {\n          // \\u221E = infinity symbol\n          Pattern p = Pattern.compile(\"\\\\A([+-]?)(Inf(inity)?|\\u221E)\\\\z\", Pattern.CASE_INSENSITIVE);\n          Matcher m = p.matcher(value.getRawValue().trim());\n          if (m.matches()){\n            if (m.group(1).equals(\"-\")){\n              return ValueTypeDouble.ValueDouble.of(Double.NEGATIVE_INFINITY);\n            }\n            return ValueTypeDouble.ValueDouble.of(Double.POSITIVE_INFINITY);\n          }\n          // Try as a long\n          return ValueTypeDouble.ValueDouble.of((double) Long.decode(value.getRawValue()));\n        } catch (NumberFormatException e2) {\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),\n                    Component.translatable(ValueTypes.DOUBLE.getTranslationKey())));\n        }\n      }\n    }));\n\n    /**\n     * Integer Parse operator which takes a string of a form Integer.decode() can consume.\n     */\n    public static final IOperator PARSE_INTEGER = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.INTEGER, v -> {\n      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);\n      try{\n        return ValueTypeInteger.ValueInteger.of(Integer.decode(value.getRawValue()));\n      } catch (NumberFormatException e) {\n          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),\n                  Component.translatable(ValueTypes.INTEGER.getTranslationKey())));\n      }\n    }));\n\n    /**\n     * Long Parse operator which takes a string of a form Long.decode() can consume.\n     */\n    public static final IOperator PARSE_LONG = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.LONG, v -> {\n      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);\n      try {\n        return ValueTypeLong.ValueLong.of(Long.decode(value.getRawValue()));\n      } catch (NumberFormatException e) {\n          throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),\n                  Component.translatable(ValueTypes.LONG.getTranslationKey())));\n      }\n    }));\n\n    /**\n     * NBT Parse operator which takes a string of a form ValueTypeNbt().deserialize() can consume.\n     */\n    public static final IOperator PARSE_NBT = Operators.REGISTRY.register(new ParseOperator<>(ValueTypes.NBT, v -> {\n      ValueTypeString.ValueString value = v.getValue(0, ValueTypes.STRING);\n      try {\n        return ValueTypeNbt.ValueNbt.of(Helpers.TAG_PARSER.parseFully(value.getRawValue()));\n      } catch (CommandSyntaxException e) {\n        throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value.getRawValue(),\n                Component.translatable(ValueTypes.NBT.getTranslationKey())));\n      }\n    }));\n\n    /**\n     * ----------------------------------- GENERAL OPERATORS -----------------------------------\n     */\n\n    /**\n     * Choice operator with one boolean input, two any inputs and one output any.\n     */\n    public static final GeneralOperator GENERAL_CHOICE = REGISTRY.register(new GeneralChoiceOperator(\"?\", \"choice\", \"choice\"));\n\n    /**\n     * Identity operator with one any input and one any output\n     */\n    public static final GeneralOperator GENERAL_IDENTITY = REGISTRY.register(new GeneralIdentityOperator(\"id\", \"identity\", \"identity\"));\n\n    /**\n     * Constant operator with two any inputs and one any output\n     */\n    public static final GeneralOperator GENERAL_CONSTANT = REGISTRY.register(new GeneralConstantOperator(\"K\", \"constant\", \"constant\"));\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/ParseOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\n\nimport java.util.function.Consumer;\n\n/**\n * Base class for parse operators.\n * @author rubensworks/LostOfThought\n */\npublic class ParseOperator<T2 extends IValueType<V2>, V2 extends IValue> extends OperatorBase {\n\n  private final T2 to;\n\n  public ParseOperator(final T2 to, IFunction operator) {\n    super(\"parse_\" + to.getTypeName(),\n            \"parse_\" + to.getTranslationKey(),\n            \"parseAs\" + Helpers.capitalizeString(to.getTypeName()),\n            null,\n            false,\n            constructInputVariables(1, ValueTypes.STRING),\n            to,\n            operator, IConfigRenderPattern.PREFIX_1_LONG);\n    this.to = to;\n  }\n\n  @Override\n  public Identifier getUniqueName() {\n    return Identifier.fromNamespaceAndPath(getModId(), \"operator.\" + getModId() + \".parse.\" + to.getTranslationKey());\n  }\n\n  @Override\n  public String getUnlocalizedType() {\n    return \"parse\";\n  }\n\n  @Override\n  protected String getUnlocalizedPrefix() {\n    return \"operator.\" + getModId() + \".\" + getUnlocalizedType();\n  }\n\n  @Override\n  public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo) {\n    tooltipAdder.accept(Component.translatable(\"operator.integrateddynamics.parse.tooltip\",\n            Component.translatable(to.getTranslationKey()))\n    );\n    super.loadTooltip(tooltipAdder, appendOptionalInfo);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\nimport org.cyclops.cyclopscore.persist.nbt.NBTClassType;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorSerializer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\n\n/**\n * An abstract operator that is based on something positioned.\n * @author rubensworks\n */\npublic abstract class PositionedOperator extends OperatorBase implements INBTProvider {\n\n    private DimPos pos;\n    private Direction side;\n\n    protected PositionedOperator(String symbol, String operatorName, String interactName, IValueType[] inputTypes, IValueType outputType,\n                                 IFunction function, IConfigRenderPattern renderPattern, DimPos pos, Direction side) {\n        super(symbol, operatorName, interactName, null, false, inputTypes, outputType, function, renderPattern);\n        this.pos = pos;\n        this.side = side;\n    }\n\n    @Override\n    public IOperator materialize() {\n        return this;\n    }\n\n    @Override\n    public void writeGeneratedFieldsToNBT(ValueOutput output) {\n        NBTClassType.writeNbt(DimPos.class, \"pos\", pos, output);\n        NBTClassType.writeNbt(Direction.class, \"side\", side, output);\n    }\n\n    @Override\n    public void readGeneratedFieldsFromNBT(ValueInput input) {\n        this.pos = NBTClassType.readNbt(DimPos.class, \"pos\", input);\n        this.side = NBTClassType.readNbt(Direction.class, \"side\", input);\n    }\n\n    public DimPos getPos() {\n        return pos;\n    }\n\n    public void setPos(DimPos pos) {\n        this.pos = pos;\n    }\n\n    public Direction getSide() {\n        return side;\n    }\n\n    public void setSide(Direction side) {\n        this.side = side;\n    }\n\n    public static class Serializer implements IOperatorSerializer<PositionedOperator> {\n\n        private final Class<? extends PositionedOperator> clazz;\n        private final Identifier uniqueName;\n\n        public Serializer(Class<? extends PositionedOperator> clazz, Identifier uniqueName) {\n            this.clazz = clazz;\n            this.uniqueName = uniqueName;\n        }\n\n        @Override\n        public boolean canHandle(IOperator operator) {\n            return this.clazz.isInstance(operator);\n        }\n\n        @Override\n        public Identifier getUniqueName() {\n            return this.uniqueName;\n        }\n\n        @Override\n        public void serialize(ValueOutput valueOutput, PositionedOperator operator) {\n            operator.writeGeneratedFieldsToNBT(valueOutput);\n        }\n\n        @Override\n        public PositionedOperator deserialize(ValueInput valueInput) throws EvaluationException {\n            try {\n                Constructor<? extends PositionedOperator> constructor = this.clazz.getConstructor();\n                PositionedOperator proxy = constructor.newInstance();\n                proxy.readGeneratedFieldsFromNBT(valueInput);\n                return proxy;\n            } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | ClassCastException | IllegalAccessException e) {\n                e.printStackTrace();\n                throw new EvaluationException(Component.translatable(L10NValues.VALUETYPE_ERROR_DESERIALIZE,\n                        valueInput.toString(), e.getMessage()));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorNetworkVariableById.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\n/**\n * An operator that gets a variable by id.\n * @author rubensworks\n */\npublic class PositionedOperatorNetworkVariableById extends PositionedOperator {\n\n    public PositionedOperatorNetworkVariableById(DimPos pos, Direction side) {\n        super(\"variablebyid\", \"variablebyid\", \"variablebyid\", new IValueType[]{ValueTypes.INTEGER}, ValueTypes.CATEGORY_ANY, new Function(), IConfigRenderPattern.PREFIX_1, pos, side);\n        ((PositionedOperatorNetworkVariableById.Function) this.getFunction()).setOperator(this);\n    }\n\n    public PositionedOperatorNetworkVariableById() {\n        this(null, null);\n        ((PositionedOperatorNetworkVariableById.Function) this.getFunction()).setOperator(this);\n    }\n\n    @Override\n    protected String getUnlocalizedType() {\n        return \"virtual\";\n    }\n\n    public static class Function implements IFunction {\n\n        private PositionedOperatorNetworkVariableById operator;\n\n        public void setOperator(PositionedOperatorNetworkVariableById operator) {\n            this.operator = operator;\n        }\n\n        public PositionedOperatorNetworkVariableById getOperator() {\n            return operator;\n        }\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            INetwork network = NetworkHelpers.getNetwork(PartPos.of(getOperator().getPos(), getOperator().getSide())).orElse(null);\n            IPartNetwork partNetwork = NetworkHelpers.getPartNetwork(network).orElse(null);\n            if (network != null && partNetwork != null) {\n                int variableId = variables.getValue(0, ValueTypes.INTEGER).getRawValue();\n                IVariableFacade variableFacade = partNetwork.getVariableFacade(variableId);\n                if (variableFacade != null) {\n                    return variableFacade.getVariable(network, partNetwork).getValue();\n                }\n                EvaluationException exception = new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(variableId)));\n                exception.setRetryEvaluation(true);\n                throw exception;\n            }\n            EvaluationException exception = new EvaluationException(Component.translatable(L10NValues.GENERAL_ERROR_NONETWORK));\n            exception.setRetryEvaluation(true);\n            throw exception;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandler.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.Direction;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * An operator related to a recipe handler.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandler<T extends IValueType<V>, V extends IValue> extends PositionedOperator {\n\n    private final String unlocalizedType;\n\n    public PositionedOperatorRecipeHandler(String name, Function function, IValueType output, DimPos pos, Direction side) {\n        super(name, name, name, new IValueType[]{ValueTypes.OBJECT_INGREDIENTS},\n                output, function, IConfigRenderPattern.PREFIX_1, pos, side);\n        this.unlocalizedType = \"virtual\";\n        ((Function) this.getFunction()).setOperator(this);\n    }\n\n    public PositionedOperatorRecipeHandler(String name, Function function, DimPos pos, Direction side) {\n        super(name, name, name, new IValueType[]{ValueTypes.OBJECT_INGREDIENTS},\n                ValueTypes.OBJECT_INGREDIENTS, function, IConfigRenderPattern.PREFIX_1, pos, side);\n        this.unlocalizedType = \"virtual\";\n        ((Function) this.getFunction()).setOperator(this);\n    }\n\n    protected Optional<IRecipeHandler> getRecipeHandler() {\n        return Optional.ofNullable(getPos().getLevel(true).getCapability(org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK, getPos().getBlockPos(), getSide()));\n    }\n\n    @Override\n    protected String getUnlocalizedType() {\n        return unlocalizedType;\n    }\n\n    @Override\n    public IOperator materialize() {\n        return this;\n    }\n\n    public static abstract class Function implements IFunction {\n\n        private PositionedOperatorRecipeHandler operator;\n\n        public void setOperator(PositionedOperatorRecipeHandler operator) {\n            this.operator = operator;\n        }\n\n        public PositionedOperatorRecipeHandler getOperator() {\n            return operator;\n        }\n    }\n\n    public static boolean validateIngredientsExact(IMixedIngredients ingredients, IMixedIngredients givenIngredients) {\n        for (IngredientComponent component : ingredients.getComponents()) {\n            List<?> actualComponents = ingredients.getInstances(component);\n            List<?> givenComponents = givenIngredients.getInstances(component);\n\n            if (actualComponents.size() != givenComponents.size()) {\n                return false;\n            }\n\n            // All components must be valid\n            for (int i = 0; i < actualComponents.size(); i++) {\n                Object actualIngredient = actualComponents.get(i);\n                Object givenIngredient = givenComponents.get(i);\n                if (!component.getMatcher().matchesExactly(givenIngredient, actualIngredient)) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    public static boolean validateIngredientsPartial(IMixedIngredients ingredients, IMixedIngredients givenIngredients) {\n        for (IngredientComponent component : ingredients.getComponents()) {\n            List<?> actualComponents = ingredients.getInstances(component);\n            List<?> givenComponents = givenIngredients.getInstances(component);\n\n            // At least all given components must match,\n            // the actual component count may be larger.\n            if (actualComponents.size() < givenComponents.size()) {\n                return false;\n            }\n\n            // All GIVEN ingredients must match,\n            // and all actual components may only be matched at most ONCE.\n            List<Integer> actualIndexBlacklist = Lists.newLinkedList();\n            for (Object givenIngredient : givenComponents) {\n                boolean match = false;\n                for (int i = 0; i < actualComponents.size(); i++) {\n                    if (!actualIndexBlacklist.contains(i)\n                            && component.getMatcher().matchesExactly(givenIngredient, actualComponents.get(i))) {\n                        match = true;\n                        break;\n                    }\n                }\n                if (!match) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandlerInputs.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * An operator that gets the input of a recipe based on an output.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandlerInputs<T extends IValueType<V>, V extends IValue> extends PositionedOperatorRecipeHandler<T, V> {\n\n    private static final Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n            ValueTypeList.ValueList> CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(20, TimeUnit.SECONDS).build();\n\n    public PositionedOperatorRecipeHandlerInputs(DimPos pos, Direction side) {\n        super(\"recipeinputsbyoutput\", new Function(), ValueTypes.LIST, pos, side);\n    }\n\n    public PositionedOperatorRecipeHandlerInputs() {\n        this(null, null);\n    }\n\n    public static class Function extends PositionedOperatorRecipeHandler.Function {\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            ValueObjectTypeIngredients.ValueIngredients ingredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n            IRecipeHandler recipeHandler = (IRecipeHandler) this.getOperator().getRecipeHandler().orElse(null);\n            if (recipeHandler != null && ingredients.getRawValue().isPresent()) {\n                Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients> key =\n                        Pair.of(Pair.of(this.getOperator().getPos(), this.getOperator().getSide()), ingredients);\n                try {\n                    return CACHE.get(key, () -> {\n                        IMixedIngredients givenIngredients = ingredients.getRawValue().get();\n                        List<ValueObjectTypeIngredients.ValueIngredients> validIngredients = Lists.newArrayList();\n                        for (IRecipeDefinition recipe : recipeHandler.getRecipes()) {\n                            IMixedIngredients outputIngredients = recipe.getOutput();\n                            // If one valid recipe is found, add to list\n                            if (validateIngredientsPartial(outputIngredients, givenIngredients)) {\n                                validIngredients.add(ValueObjectTypeIngredients.ValueIngredients.of(\n                                        MixedIngredients.fromRecipeInput(recipe)));\n                            }\n                        }\n                        return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_INGREDIENTS, validIngredients);\n                    });\n                } catch (ExecutionException e) {\n\n                }\n            }\n            return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_INGREDIENTS, Collections.emptyList());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandlerOutput.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * An operator that gets the output of a recipe based on an input.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandlerOutput<T extends IValueType<V>, V extends IValue> extends PositionedOperatorRecipeHandler<T, V> {\n\n    private static final Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n            ValueObjectTypeIngredients.ValueIngredients> CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(20, TimeUnit.SECONDS).build();\n\n    public PositionedOperatorRecipeHandlerOutput(DimPos pos, Direction side) {\n        super(\"recipeoutputbyinput\", new Function(), pos, side);\n    }\n\n    public PositionedOperatorRecipeHandlerOutput() {\n        this(null, null);\n    }\n\n    public static class Function extends PositionedOperatorRecipeHandler.Function {\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            ValueObjectTypeIngredients.ValueIngredients ingredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n            IRecipeHandler recipeHandler = (IRecipeHandler) this.getOperator().getRecipeHandler().orElse(null);\n            if (recipeHandler != null && ingredients.getRawValue().isPresent()) {\n                Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients> key =\n                        Pair.of(Pair.of(this.getOperator().getPos(), this.getOperator().getSide()), ingredients);\n                try {\n                    return CACHE.get(key, () -> {\n                        IMixedIngredients output = recipeHandler.simulate(ingredients.getRawValue().get());\n                        return ValueObjectTypeIngredients.ValueIngredients.of(output);\n                    });\n                } catch (ExecutionException e) {\n\n                }\n            }\n            return ValueObjectTypeIngredients.ValueIngredients.of(null);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandlerRecipeByInput.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * An operator that gets the first recipes based on an input.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandlerRecipeByInput<T extends IValueType<V>, V extends IValue>\n        extends PositionedOperatorRecipeHandler<T, V> {\n\n    private static final Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n            ValueObjectTypeRecipe.ValueRecipe> CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(20, TimeUnit.SECONDS).build();\n\n    public PositionedOperatorRecipeHandlerRecipeByInput(DimPos pos, Direction side) {\n        super(\"recipebyinput\", new Function(), ValueTypes.OBJECT_RECIPE, pos, side);\n    }\n\n    public PositionedOperatorRecipeHandlerRecipeByInput() {\n        this(null, null);\n    }\n\n    public static class Function extends PositionedOperatorRecipeHandlerRecipeByOutput.Function {\n\n        protected boolean validateIngredients(IMixedIngredients actualIngredients, IMixedIngredients givenIngredients) {\n            return validateIngredientsExact(actualIngredients, givenIngredients);\n        }\n\n        @Override\n        protected Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>, ValueObjectTypeRecipe.ValueRecipe> getCache() {\n            return CACHE;\n        }\n\n        @Override\n        protected IMixedIngredients getRecipeIngredients(IRecipeDefinition recipeDefinition) {\n            return MixedIngredients.fromRecipeInput(recipeDefinition);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandlerRecipeByOutput.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * An operator that gets the first recipe based on an output.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandlerRecipeByOutput<T extends IValueType<V>, V extends IValue>\n        extends PositionedOperatorRecipeHandler<T, V> {\n\n    private static final Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n            ValueObjectTypeRecipe.ValueRecipe> CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(20, TimeUnit.SECONDS).build();\n\n    public PositionedOperatorRecipeHandlerRecipeByOutput(DimPos pos, Direction side) {\n        super(\"recipebyoutput\", new Function(), ValueTypes.OBJECT_RECIPE, pos, side);\n    }\n\n    public PositionedOperatorRecipeHandlerRecipeByOutput() {\n        this(null, null);\n    }\n\n    public static class Function extends PositionedOperatorRecipeHandler.Function {\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            ValueObjectTypeIngredients.ValueIngredients ingredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n            IRecipeHandler recipeHandler = (IRecipeHandler) this.getOperator().getRecipeHandler().orElse(null);\n            if (recipeHandler != null && ingredients.getRawValue().isPresent()) {\n                Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients> key =\n                        Pair.of(Pair.of(this.getOperator().getPos(), this.getOperator().getSide()), ingredients);\n                try {\n                    return getCache().get(key, () -> {\n                        IMixedIngredients givenIngredients = ingredients.getRawValue().get();\n                        for (IRecipeDefinition recipe : recipeHandler.getRecipes()) {\n                            IMixedIngredients outputIngredients = getRecipeIngredients(recipe);\n                            // If one valid recipe is found, return it\n                            if (recipe.getInputComponents().size() > 0\n                                    && recipe.getOutput().getComponents().size() > 0\n                                    && validateIngredients(outputIngredients, givenIngredients)) {\n                                return ValueObjectTypeRecipe.ValueRecipe.of(recipe);\n                            }\n                        }\n                        return ValueObjectTypeRecipe.ValueRecipe.of(null);\n                    });\n                } catch (ExecutionException e) {\n\n                }\n            }\n            return ValueObjectTypeRecipe.ValueRecipe.of(null);\n        }\n\n        protected boolean validateIngredients(IMixedIngredients actualIngredients, IMixedIngredients givenIngredients) {\n            return validateIngredientsPartial(actualIngredients, givenIngredients);\n        }\n\n        protected Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n                ValueObjectTypeRecipe.ValueRecipe> getCache() {\n            return CACHE;\n        }\n\n        protected IMixedIngredients getRecipeIngredients(IRecipeDefinition recipeDefinition) {\n            return recipeDefinition.getOutput();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandlerRecipesByInput.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * An operator that gets the recipes based on an input.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandlerRecipesByInput<T extends IValueType<V>, V extends IValue>\n        extends PositionedOperatorRecipeHandler<T, V> {\n\n    private static final Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n            ValueTypeList.ValueList> CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(20, TimeUnit.SECONDS).build();\n\n    public PositionedOperatorRecipeHandlerRecipesByInput(DimPos pos, Direction side) {\n        super(\"recipesbyinput\", new Function(), ValueTypes.LIST, pos, side);\n    }\n\n    public PositionedOperatorRecipeHandlerRecipesByInput() {\n        this(null, null);\n    }\n\n    public static class Function extends PositionedOperatorRecipeHandlerRecipesByOutput.Function {\n\n        protected boolean validateIngredients(IMixedIngredients actualIngredients, IMixedIngredients givenIngredients) {\n            return validateIngredientsExact(actualIngredients, givenIngredients);\n        }\n\n        @Override\n        protected Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>, ValueTypeList.ValueList> getCache() {\n            return CACHE;\n        }\n\n        @Override\n        protected IMixedIngredients getRecipeIngredients(IRecipeDefinition recipeDefinition) {\n            return MixedIngredients.fromRecipeInput(recipeDefinition);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PositionedOperatorRecipeHandlerRecipesByOutput.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.Direction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * An operator that gets the recipes based on an output.\n * @author rubensworks\n */\npublic class PositionedOperatorRecipeHandlerRecipesByOutput<T extends IValueType<V>, V extends IValue>\n        extends PositionedOperatorRecipeHandler<T, V> {\n\n    private static final Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n            ValueTypeList.ValueList> CACHE = CacheBuilder.newBuilder()\n            .expireAfterAccess(20, TimeUnit.SECONDS).build();\n\n    public PositionedOperatorRecipeHandlerRecipesByOutput(DimPos pos, Direction side) {\n        super(\"recipesbyoutput\", new Function(), ValueTypes.LIST, pos, side);\n    }\n\n    public PositionedOperatorRecipeHandlerRecipesByOutput() {\n        this(null, null);\n    }\n\n    public static class Function extends PositionedOperatorRecipeHandler.Function {\n\n        @Override\n        public IValue evaluate(SafeVariablesGetter variables) throws EvaluationException {\n            ValueObjectTypeIngredients.ValueIngredients ingredients = variables.getValue(0, ValueTypes.OBJECT_INGREDIENTS);\n            IRecipeHandler recipeHandler = (IRecipeHandler) this.getOperator().getRecipeHandler().orElse(null);\n            if (recipeHandler != null && ingredients.getRawValue().isPresent()) {\n                Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients> key =\n                        Pair.of(Pair.of(this.getOperator().getPos(), this.getOperator().getSide()), ingredients);\n                try {\n                    return getCache().get(key, () -> {\n                        IMixedIngredients givenIngredients = ingredients.getRawValue().get();\n                        List<ValueObjectTypeRecipe.ValueRecipe> validRecipes = Lists.newArrayList();\n                        for (IRecipeDefinition recipe : recipeHandler.getRecipes()) {\n                            IMixedIngredients outputIngredients = getRecipeIngredients(recipe);\n                            // If one valid recipe is found, add to list\n                            if (recipe.getInputComponents().size() > 0\n                                    && recipe.getOutput().getComponents().size() > 0\n                                    && validateIngredients(outputIngredients, givenIngredients)) {\n                                validRecipes.add(ValueObjectTypeRecipe.ValueRecipe.of(recipe));\n                            }\n                        }\n                        return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_RECIPE, validRecipes);\n                    });\n                } catch (ExecutionException e) {\n\n                }\n            }\n            return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_RECIPE, Collections.emptyList());\n        }\n\n        protected boolean validateIngredients(IMixedIngredients actualIngredients, IMixedIngredients givenIngredients) {\n            return validateIngredientsPartial(actualIngredients, givenIngredients);\n        }\n\n        protected Cache<Pair<Pair<DimPos, Direction>, ValueObjectTypeIngredients.ValueIngredients>,\n                ValueTypeList.ValueList> getCache() {\n            return CACHE;\n        }\n\n        protected IMixedIngredients getRecipeIngredients(IRecipeDefinition recipeDefinition) {\n            return recipeDefinition.getOutput();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/operator/PredicateOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.operator;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperatorSerializer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\n/**\n * An operator that wraps around a predicate.\n * @author rubensworks\n */\npublic class PredicateOperator<T extends IValueType<V>, V extends IValue> extends OperatorBase {\n\n    private final String unlocalizedType;\n    private final T inputType;\n    private final List<V> rawValues;\n\n    public PredicateOperator(T inputType, List<V> rawValues) {\n        this(rawValues::contains, inputType, rawValues);\n    }\n\n    public PredicateOperator(Predicate<V> predicate, T inputType, List<V> rawValues) {\n        super(\"pred\", \"pred\", \"pred\", null, false,\n                new IValueType[]{inputType}, ValueTypes.BOOLEAN, variables -> ValueTypeBoolean.ValueBoolean.of(\n                        predicate.test(variables.getValue(0, inputType))), IConfigRenderPattern.PREFIX_1);\n        this.inputType = inputType;\n        this.rawValues = rawValues;\n        this.unlocalizedType = \"predicate\";\n    }\n\n    @Override\n    protected String getUnlocalizedType() {\n        return unlocalizedType;\n    }\n\n    @Override\n    public IOperator materialize() {\n        return this;\n    }\n\n    public static class Serializer implements IOperatorSerializer<PredicateOperator<IValueType<IValue>, IValue>> {\n        @Override\n        public boolean canHandle(IOperator operator) {\n            return operator instanceof PredicateOperator;\n        }\n\n        @Override\n        public Identifier getUniqueName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"predicate\");\n        }\n\n        @Override\n        public void serialize(ValueOutput valueOutput, PredicateOperator<IValueType<IValue>, IValue> operator) {\n            valueOutput.putString(\"valueType\", operator.inputType.getTranslationKey());\n            ValueOutput.ValueOutputList list = valueOutput.childrenList(\"values\");\n            for (IValue rawValue : operator.rawValues) {\n                operator.inputType.serialize(list.addChild(), rawValue);\n            }\n        }\n\n        @Override\n        public PredicateOperator<IValueType<IValue>, IValue> deserialize(ValueInput valueInput) throws EvaluationException {\n            IValueType<IValue> valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(valueInput.getString(\"valueType\").orElseThrow()));\n            ValueInput.ValueInputList list = valueInput.childrenList(\"values\").orElseThrow();\n            List<IValue> values = Lists.newArrayList();\n            for (ValueInput subTag : list) {\n                values.add(ValueHelpers.deserializeRaw(subTag, valueType));\n            }\n            return new PredicateOperator<>(valueType, values);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueCastRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\n/**\n * Base implementation of a variable.\n * @author rubensworks\n */\npublic abstract class ValueBase implements IValue {\n\n    private final IValueType type;\n\n    protected ValueBase(IValueType type) {\n        this.type = type;\n    }\n\n    @Override\n    public IValueType getType() {\n        return type;\n    }\n\n    protected IValueCastRegistry getValueCastRegistry() {\n        return ValueCastMappings.REGISTRY;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <V extends IValue> V cast(IValueType<V> valueType) throws IValueCastRegistry.ValueCastException {\n        if(valueType == getType()) {\n            return (V) this;\n        } else {\n            return getValueCastRegistry().cast(valueType, this);\n        }\n    }\n\n    @Override\n    public <V extends IValue> boolean canCast(IValueType<V> valueType) {\n        return getValueCastRegistry().canCast(valueType, this);\n    }\n\n    @Override\n    public String toString() {\n        return \"ValueBase(type=\" + this.type + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueCastMappings.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueCastRegistry;\n\n/**\n * Collection of variable types.\n * @author rubensworks\n */\npublic class ValueCastMappings {\n\n    public static final IValueCastRegistry REGISTRY = constructRegistry();\n\n    private static IValueCastRegistry constructRegistry() {\n        // This also allows this registry to be used outside of a minecraft environment.\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IValueCastRegistry.class);\n        } else {\n            return ValueCastRegistry.getInstance();\n        }\n    }\n\n    public static void load() {\n        if (REGISTRY.size() > 0) {\n            return;\n        }\n\n        REGISTRY.register(ValueTypes.INTEGER, ValueTypes.DOUBLE, new IValueCastRegistry.IMapping<ValueTypeInteger, ValueTypeDouble, ValueTypeInteger.ValueInteger, ValueTypeDouble.ValueDouble>() {\n            @Override\n            public ValueTypeDouble.ValueDouble cast(ValueTypeInteger.ValueInteger value) {\n                return ValueTypeDouble.ValueDouble.of((double) value.getRawValue());\n            }\n        });\n        REGISTRY.register(ValueTypes.INTEGER, ValueTypes.LONG, new IValueCastRegistry.IMapping<ValueTypeInteger, ValueTypeLong, ValueTypeInteger.ValueInteger, ValueTypeLong.ValueLong>() {\n            @Override\n            public ValueTypeLong.ValueLong cast(ValueTypeInteger.ValueInteger value) {\n                return ValueTypeLong.ValueLong.of((long) value.getRawValue());\n            }\n        });\n        REGISTRY.register(ValueTypes.DOUBLE, ValueTypes.INTEGER, new IValueCastRegistry.IMapping<ValueTypeDouble, ValueTypeInteger, ValueTypeDouble.ValueDouble, ValueTypeInteger.ValueInteger>() {\n            @Override\n            public ValueTypeInteger.ValueInteger cast(ValueTypeDouble.ValueDouble value) {\n                return ValueTypeInteger.ValueInteger.of((int) value.getRawValue());\n            }\n        });\n        REGISTRY.register(ValueTypes.DOUBLE, ValueTypes.LONG, new IValueCastRegistry.IMapping<ValueTypeDouble, ValueTypeLong, ValueTypeDouble.ValueDouble, ValueTypeLong.ValueLong>() {\n            @Override\n            public ValueTypeLong.ValueLong cast(ValueTypeDouble.ValueDouble value) {\n                return ValueTypeLong.ValueLong.of((long) value.getRawValue());\n            }\n        });\n        REGISTRY.register(ValueTypes.LONG, ValueTypes.INTEGER, new IValueCastRegistry.IMapping<ValueTypeLong, ValueTypeInteger, ValueTypeLong.ValueLong, ValueTypeInteger.ValueInteger>() {\n            @Override\n            public ValueTypeInteger.ValueInteger cast(ValueTypeLong.ValueLong value) {\n                return ValueTypeInteger.ValueInteger.of((int) value.getRawValue());\n            }\n        });\n        REGISTRY.register(ValueTypes.LONG, ValueTypes.DOUBLE, new IValueCastRegistry.IMapping<ValueTypeLong, ValueTypeDouble, ValueTypeLong.ValueLong, ValueTypeDouble.ValueDouble>() {\n            @Override\n            public ValueTypeDouble.ValueDouble cast(ValueTypeLong.ValueLong value) {\n                return ValueTypeDouble.ValueDouble.of((double) value.getRawValue());\n            }\n        });\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueCastRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Maps;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueCastRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.CastOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\nimport java.util.Map;\n\n/**\n * Registry for casting {@link IValue}.\n * @author rubensworks\n */\npublic final class ValueCastRegistry implements IValueCastRegistry {\n\n    private static ValueCastRegistry INSTANCE = new ValueCastRegistry();\n\n    private final Map<Pair<IValueType, IValueType>, IMapping> mappings = Maps.newHashMap();\n\n    private ValueCastRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static ValueCastRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public int size() {\n        return this.mappings.size();\n    }\n\n    @Override\n    public <T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> void register(T1 from, T2 to, IMapping<T1, T2, V1, V2> mapping) {\n        mappings.put(Pair.<IValueType, IValueType>of(from, to), mapping);\n        Operators.REGISTRY.register(new CastOperator<>(from, to, mapping));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> V2 cast(T2 target, V1 value) throws ValueCastException {\n        IMapping mapping = mappings.get(Pair.<IValueType, IValueType>of(value.getType(), target));\n        if(mapping == null) {\n            throw new ValueCastException(value.getType(), target);\n        }\n        return ((IMapping<T1, T2, V1, V2>) mapping).cast(value);\n    }\n\n    @Override\n    public <T1 extends IValueType<V1>, T2 extends IValueType<V2>, V1 extends IValue, V2 extends IValue> boolean canCast(T2 target, V1 value) {\n        return mappings.containsKey(Pair.<IValueType, IValueType>of(value.getType(), target));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.IdentifierException;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeCategory;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.CurriedOperator;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport javax.annotation.Nullable;\n\n/**\n * A collection of helpers for variables, values and value types.\n * @author rubensworks\n */\npublic class ValueHelpers {\n\n    /**\n     * Create a new value type array from the given variable array element-wise.\n     * If a variable would be null, that corresponding value type would be null as well.\n     * @param variables The variables.\n     * @return The value types array corresponding element-wise to the variables array.\n     */\n    public static IValueType[] from(IVariable... variables) {\n        IValueType[] valueTypes = new IValueType[variables.length];\n        for(int i = 0; i < valueTypes.length; i++) {\n            IVariable variable = variables[i];\n            valueTypes[i] = variable == null ? null : variable.getType();\n        }\n        return valueTypes;\n    }\n\n    /**\n     * Create a new value type array from the given variableFacades array element-wise.\n     * If a variableFacade would be null, that corresponding value type would be null as well.\n     * @param variableFacades The variables facades.\n     * @return The value types array corresponding element-wise to the variables array.\n     */\n    public static IValueType<?>[] from(IVariableFacade... variableFacades) {\n        IValueType<?>[] valueTypes = new IValueType[variableFacades.length];\n        for(int i = 0; i < valueTypes.length; i++) {\n            IVariableFacade variableFacade = variableFacades[i];\n            valueTypes[i] = variableFacade == null ? null : variableFacade.getOutputType();\n        }\n        return valueTypes;\n    }\n\n    /**\n     * Create a new unlocalized name array from the given variableFacades array element-wise.\n     * @param valueTypes The value types.\n     * @return The unlocalized names array corresponding element-wise to the value types array.\n     */\n    public static Component[] from(IValueType<?>... valueTypes) {\n        Component[] names = new Component[valueTypes.length];\n        for(int i = 0; i < valueTypes.length; i++) {\n            IValueType<?> valueType = valueTypes[i];\n            names[i] = Component.translatable(valueType.getTranslationKey());\n        }\n        return names;\n    }\n\n    /**\n     * Check if the two given values are equal.\n     * If they are both null, they are also considered equal.\n     * @param v1 Value one\n     * @param v2 Value two\n     * @return If they are equal.\n     */\n    public static boolean areValuesEqual(@Nullable IValue v1, @Nullable IValue v2) {\n        return v1 == null && v2 == null || (!(v1 == null || v2 == null) && v1.equals(v2));\n    }\n\n    /**\n     * Bidirectional checking of correspondence.\n     * @param t1 First type.\n     * @param t2 Second type.\n     * @return If they correspond to each other in some direction.\n     */\n    public static boolean correspondsTo(IValueType t1, IValueType t2) {\n        return t1.correspondsTo(t2) || t2.correspondsTo(t1);\n    }\n\n    /**\n     * Check if the variable corresponds to the given type.\n     * @param variable Variable.\n     * @param type First type.\n     * @return If they correspond to each.\n     */\n    public static boolean correspondsTo(IVariable<?> variable, IValueType type) {\n        // Special case when the variable type is ANY, but the actual contained value is more specific.\n        // This can for example occur in Integrated Scripting when applying a scripted operator.\n        // See CyclopsMC/IntegratedScripting#20\n        if (type != ValueTypes.CATEGORY_ANY && variable.getType() == ValueTypes.CATEGORY_ANY) {\n            try {\n                IValue value = variable.getValue();\n                return ValueHelpers.correspondsTo(type, value.getType());\n            } catch (EvaluationException e) {\n                // Ignore error\n                return false;\n            }\n        }\n\n        return ValueHelpers.correspondsTo(type, variable.getType());\n    }\n\n    /**\n     * Evaluate an operator for the given values.\n     * @param operator The operator.\n     * @param values The values.\n     * @return The resulting value.\n     * @throws EvaluationException If something went wrong during operator evaluation.\n     */\n    public static IValue evaluateOperator(IOperator operator, IValue... values) throws EvaluationException {\n        IVariable[] variables = new IVariable[values.length];\n        for (int i = 0; i < variables.length; i++) {\n            IValue value = values[i];\n            variables[i] = new Variable<>(value.getType(), value);\n        }\n        return ValueHelpers.evaluateOperator(operator, variables);\n    }\n\n    /**\n     * Evaluate an operator for the given variables.\n     * @param operator The operator.\n     * @param variables The variables.\n     * @return The resulting value.\n     * @throws EvaluationException If something went wrong during operator evaluation.\n     */\n    public static IValue evaluateOperator(IOperator operator, IVariable... variables) throws EvaluationException {\n        int requiredLength = operator.getRequiredInputLength();\n        if (requiredLength == variables.length) {\n            return operator.evaluate(variables);\n        } else {\n            if (variables.length > requiredLength) { // We have MORE variables as input than the operator accepts\n                IVariable[] acceptableVariables = ArrayUtils.subarray(variables, 0, requiredLength);\n                IVariable[] remainingVariables = ArrayUtils.subarray(variables, requiredLength, variables.length);\n\n                // Pass all required variables to the operator, and forward all remaining ones to the resulting operator\n                IValue result = evaluateOperator(operator, acceptableVariables);\n\n                // Error if the result is NOT an operator\n                if (result.getType() != ValueTypes.OPERATOR) {\n                    throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_CURRYINGOVERFLOW,\n                            Component.translatable(operator.getTranslationKey()),\n                            requiredLength,\n                            variables.length,\n                            Component.translatable(result.getType().getTranslationKey())));\n                }\n\n                // Pass all remaining variables to the resulting operator\n                IOperator nextOperator = ((ValueTypeOperator.ValueOperator) result).getRawValue();\n                return evaluateOperator(nextOperator, remainingVariables);\n\n            } else { // Else, the given variables only partially take up the required input\n                return ValueTypeOperator.ValueOperator.of(new CurriedOperator(operator, variables));\n            }\n        }\n    }\n\n    /**\n     * Serialize the given value to a raw tag without its value type.\n     *\n     * @param valueOutput The value output.\n     * @param value       The value.\n     */\n    public static void serializeRaw(ValueOutput valueOutput, IValue value) {\n        value.getType().serialize(valueOutput, value);\n    }\n\n    /**\n     * Serialize the given value.\n     *\n     * @param valueOutput The output to write to.\n     * @param value  The value.\n     */\n    public static void serialize(ValueOutput valueOutput, IValue value) {\n        valueOutput.putString(\"valueType\", value.getType().getUniqueName().toString());\n        serializeRaw(valueOutput, value);\n    }\n\n    /**\n     * Deserialize the given input to a value.\n     *\n     * @param input The input containing a value.\n     * @return The value.\n     */\n    public static IValue deserialize(ValueInput input) {\n        IValueType valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(input.getString(\"valueType\").orElseThrow()));\n        if (valueType == null) {\n            return null;\n        }\n        return deserializeRaw(input, valueType);\n    }\n\n    /**\n     * Deserialize the given value string to a value.\n     *\n     * @param <T>        The type of value.\n     * @param valueInput The value input.\n     * @param valueType  The value type to deserialize for.\n     * @return The value.\n     */\n    public static <T extends IValue> T deserializeRaw(ValueInput valueInput, IValueType<T> valueType) {\n        return valueType.deserialize(valueInput);\n    }\n\n    /**\n     * Get the string representation of the given value.\n     * This is useful for cases when the value needs to be edited in a GUI.\n     *\n     * This corresponds to {@link #parseString(IValueType, String)}.\n     *\n     * @param value A value.\n     * @param <T> The value type.\n     * @return A string representation of the given value.\n     */\n    public static <T extends IValue> String toString(T value) {\n        return value.getType().toString(value);\n    }\n\n    /**\n     * Parse the given string representation of a value.\n     *\n     * This corresponds to {@link #toString(IValue)}.\n     *\n     * @param valueType The value type to parse by.\n     * @param value A string representation of a value.\n     * @param <T> The value type.\n     * @return A value.\n     * @throws EvaluationException If parsing failed.\n     */\n    public static <T extends IValue> T parseString(IValueType<T> valueType, String value) throws EvaluationException {\n        return valueType.parseString(value);\n    }\n\n    /**\n     * Check if the given result (from the given operator) is a boolean.\n     * @param predicate A predicate, used for error logging.\n     * @param result A result from the given predicate\n     * @throws EvaluationException If the value was not a boolean.\n     */\n    public static void validatePredicateOutput(IOperator predicate, IValue result) throws EvaluationException {\n        if (!(result instanceof ValueTypeBoolean.ValueBoolean)) {\n            MutableComponent error = Component.translatable(\n                    L10NValues.OPERATOR_ERROR_WRONGPREDICATE,\n                    predicate.getLocalizedNameFull(),\n                    Component.translatable(result.getType().getTranslationKey()),\n                    Component.translatable(ValueTypes.BOOLEAN.getTranslationKey()));\n            throw new EvaluationException(error);\n        }\n    }\n\n    /**\n     * Get the human readable value of the given value in a safe way.\n     * @param variable A nullable variable.\n     * @return A pair of a string and color.\n     */\n    public static Pair<MutableComponent, Integer> getSafeReadableValue(@Nullable IVariable variable) {\n        MutableComponent readValue = Component.literal(\"\");\n        int readValueColor = 0;\n        if (!NetworkHelpers.shouldWork()) {\n            readValue = Component.literal(\"SAFE-MODE\");\n        } else if(variable != null) {\n            try {\n                IValue value = variable.getValue();\n                readValue = value.getType().toCompactString(value);\n                readValueColor = value.getType().getDisplayColor();\n            } catch (EvaluationException | NullPointerException | PartStateException e) {\n                readValue = Component.literal(\"ERROR\");\n                readValueColor = IModHelpers.get().getBaseHelpers().RGBToInt(255, 0, 0);\n            }\n        }\n        return Pair.of(readValue, readValueColor);\n    }\n\n    /**\n     * Create a Identifier from the given value.\n     * Any IdentifierExceptions will be emitted as EvaluationException.\n     * @param value A Identifier value.\n     * @return A Identifier\n     * @throws EvaluationException If a IdentifierException was thrown.\n     */\n    public static Identifier createIdentifierInEvaluation(String value) throws EvaluationException {\n        try {\n            return Identifier.parse(value);\n        } catch (IdentifierException e) {\n            throw new EvaluationException(Component.literal(e.getMessage()));\n        }\n    }\n\n    /**\n     * If the given variable has type ANY, attempt to cast the type to the given category type, or throw.\n     * @param variable The variable.\n     * @param operator An operator to include in the error message.\n     * @param category The category to check.\n     * @param categoryClazz The category class.\n     * @param <V> The value type.\n     * @param <C> The category type.\n     * @return The cast value type.\n     * @throws EvaluationException If casting failed.\n     */\n    public static <V extends IValue, C extends IValueType<V>> C variableUnpackAnyType(\n            IVariable variable, IOperator operator, IValueTypeCategory<V> category, Class<? super C> categoryClazz)\n            throws EvaluationException {\n        IValueType type = variable.getType();\n        if (type == ValueTypes.CATEGORY_ANY) {\n            type = variable.getValue().getType();\n            if (!categoryClazz.isInstance(type)) {\n                throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        operator.getLocalizedNameFull(),\n                        Component.translatable(type.getTranslationKey()),\n                        \"0\",\n                        Component.translatable(category.getTranslationKey())));\n            }\n        }\n        return (C) type;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\n\nimport javax.annotation.Nullable;\n\n/**\n * Base implementation of a value object type.\n * @author rubensworks\n */\npublic abstract class ValueObjectTypeBase<V extends IValue> extends ValueTypeBase<V> {\n\n    public ValueObjectTypeBase(String typeName, @Nullable Class<V> valueClass) {\n        this(typeName, IModHelpers.get().getBaseHelpers().RGBToInt(243, 243, 243), ChatFormatting.GRAY, valueClass);\n    }\n\n    public ValueObjectTypeBase(String typeName, int color, ChatFormatting colorFormat, @Nullable Class<V> valueClass) {\n        super(typeName, color, colorFormat, valueClass);\n    }\n\n    @Override\n    public boolean isObject() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeBlock.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.BlockItem;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeUniquelyNamed;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeItemStackLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\n/**\n * Value type with values that are blocks (these are internally stored as blockstates).\n * @author rubensworks\n */\npublic class ValueObjectTypeBlock extends ValueObjectTypeBase<ValueObjectTypeBlock.ValueBlock> implements\n        IValueTypeNamed<ValueObjectTypeBlock.ValueBlock>, IValueTypeUniquelyNamed<ValueObjectTypeBlock.ValueBlock>,\n        IValueTypeNullable<ValueObjectTypeBlock.ValueBlock> {\n\n    public ValueObjectTypeBlock() {\n        super(\"block\", ValueObjectTypeBlock.ValueBlock.class);\n    }\n\n    public static MutableComponent getBlockkDisplayNameSafe(BlockState blockState) {\n        return Component.translatable(blockState.getBlock().getDescriptionId());\n    }\n\n    @Override\n    public ValueBlock getDefault() {\n        return ValueBlock.of(Blocks.AIR.defaultBlockState());\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueBlock value) {\n        if (value.getRawValue().isPresent()) {\n            BlockState blockState = value.getRawValue().get();\n            ItemStack itemStack = IModHelpers.get().getBlockHelpers().getItemStackFromBlockState(blockState);\n            if (!itemStack.isEmpty()) {\n                return ValueObjectTypeItemStack.getItemStackDisplayNameSafe(itemStack);\n            }\n            return ValueObjectTypeBlock.getBlockkDisplayNameSafe(blockState);\n        }\n        return Component.literal(\"\");\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueBlock value) {\n        value.getRawValue().ifPresent(v -> valueOutput.store(\"v\", BlockState.CODEC, v));\n    }\n\n    @Override\n    public ValueBlock deserialize(ValueInput valueInput) {\n        return ValueBlock.of(valueInput.read(\"v\", BlockState.CODEC).orElseGet(Blocks.AIR::defaultBlockState));\n    }\n\n    @Override\n    public String getName(ValueBlock a) {\n        return toCompactString(a).getString();\n    }\n\n    @Override\n    public boolean isNull(ValueBlock a) {\n        return !a.getRawValue().isPresent();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeItemStackLPElement<>(this, new ValueTypeItemStackLPElement.IItemStackToValue<ValueObjectTypeBlock.ValueBlock>() {\n            @Override\n            public boolean isNullable() {\n                return true;\n            }\n\n            @Override\n            public Component validate(ItemStack itemStack) {\n                if(!itemStack.isEmpty() && !(itemStack.getItem() instanceof BlockItem)) {\n                    return Component.translatable(L10NValues.VALUETYPE_OBJECT_BLOCK_ERROR_NOBLOCK);\n                }\n                return null;\n            }\n\n            @Override\n            public ValueObjectTypeBlock.ValueBlock getValue(ItemStack itemStack) {\n                return ValueObjectTypeBlock.ValueBlock.of(\n                        itemStack.isEmpty() ? Blocks.AIR.defaultBlockState() : IModHelpers.get().getBlockHelpers().getBlockStateFromItemStack(itemStack));\n            }\n\n            @Override\n            public ItemStack getValueAsItemStack(ValueBlock value) {\n                return value.getRawValue()\n                        .map(IModHelpers.get().getBlockHelpers()::getItemStackFromBlockState)\n                        .orElse(ItemStack.EMPTY);\n            }\n        });\n    }\n\n    @Override\n    public String getUniqueName(ValueBlock value) {\n        if (value.getRawValue().isPresent()) {\n            BlockState blockState = value.getRawValue().get();\n            return BuiltInRegistries.BLOCK.getKey(blockState.getBlock()).toString();\n        }\n        return \"\";\n    }\n\n    public static class ValueBlock extends ValueOptionalBase<BlockState> {\n\n        private ValueBlock(BlockState blockState) {\n            super(ValueTypes.OBJECT_BLOCK, blockState);\n        }\n\n        public static ValueBlock of(BlockState blockState) {\n            return new ValueBlock(blockState);\n        }\n\n        @Override\n        protected boolean isEqual(BlockState a, BlockState b) {\n            return a.equals(b);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueBlock()\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeEntity.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeUniquelyNamed;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.UUID;\n\n/**\n * Value type with values that are itemstacks.\n * @author rubensworks\n */\npublic class ValueObjectTypeEntity extends ValueObjectTypeBase<ValueObjectTypeEntity.ValueEntity> implements\n        IValueTypeNamed<ValueObjectTypeEntity.ValueEntity>, IValueTypeUniquelyNamed<ValueObjectTypeEntity.ValueEntity>,\n        IValueTypeNullable<ValueObjectTypeEntity.ValueEntity> {\n\n    public ValueObjectTypeEntity() {\n        super(\"entity\", ValueObjectTypeEntity.ValueEntity.class);\n    }\n\n    @Override\n    public ValueEntity getDefault() {\n        return ValueEntity.of((UUID) null);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueEntity value) {\n        Optional<UUID> uuid = value.getUuid();\n        if (uuid.isPresent()) {\n            Optional<Entity> entity = value.getRawValue();\n            if(entity.isPresent()) {\n                Entity e = entity.get();\n                if(e instanceof ItemEntity) {\n                    return (MutableComponent) ((ItemEntity) e).getItem().getHoverName();\n                } else {\n                    return (MutableComponent) e.getName();\n                }\n            }\n            return Component.literal(\"unknown\");\n        }\n        return Component.literal(\"\");\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueEntity value) {\n        value.getUuid().ifPresent(v -> valueOutput.putString(\"v\", v.toString()));\n    }\n\n    @Override\n    public ValueEntity deserialize(ValueInput valueInput) {\n        return ValueEntity.of(valueInput.getString(\"v\").map(UUID::fromString).orElse(null));\n    }\n\n    @Override\n    public String getName(ValueEntity a) {\n        return toCompactString(a).getString();\n    }\n\n    @Override\n    public boolean isNull(ValueEntity a) {\n        return !a.getRawValue().isPresent();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return null;\n    }\n\n    @Override\n    public String getUniqueName(ValueEntity value) {\n        Optional<UUID> uuid = value.getUuid();\n        if (uuid.isPresent()) {\n            UUID id = uuid.get();\n            String entityName = value.getRawValue()\n                    .map(entity -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString())\n                    .orElse(\"unknown\");\n            return id.toString() + \" (\" + entityName + \")\";\n        }\n        return \"\";\n    }\n\n    public static class ValueEntity extends ValueBase {\n\n        private final Optional<UUID> value;\n\n        protected ValueEntity(@Nullable Entity value) {\n            super(ValueTypes.OBJECT_ENTITY);\n            this.value = value == null ? Optional.<UUID>empty() : Optional.of(value.getUUID());\n        }\n\n        private ValueEntity(@Nullable UUID entityUuid) {\n            super(ValueTypes.OBJECT_ENTITY);\n            this.value = Optional.ofNullable(entityUuid);\n        }\n\n        /**\n         * @return The raw value in an optional holder.\n         */\n        public Optional<Entity> getRawValue() {\n            Optional<UUID> uuid = getUuid();\n            if (uuid.isPresent()) {\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                    return ValueObjectTypeEntityClient.getEntity(uuid.get());\n                }\n                for (ServerLevel world : ServerLifecycleHooks.getCurrentServer().getAllLevels()) {\n                    Entity entity = world.getEntity(uuid.get());\n                    if (entity != null) {\n                        return Optional.of(entity);\n                    }\n                }\n            }\n            return Optional.empty();\n        }\n\n        public Optional<UUID> getUuid() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if(o instanceof ValueEntity) {\n                if (((ValueEntity) o).value.isPresent() && value.isPresent()) {\n                    return ((ValueEntity) o).value.get().equals(value.get());\n                } else if (!((ValueEntity) o).value.isPresent() && !value.isPresent()) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        @Override\n        public int hashCode() {\n            return getType().hashCode() + (getRawValue().isPresent() ? getRawValue().get().hashCode() : 0);\n        }\n\n        public static ValueEntity of(@Nullable Entity entity) {\n            return new ValueEntity(entity);\n        }\n\n        public static ValueEntity of(@Nullable UUID entityUuid) {\n            return new ValueEntity(entityUuid);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueEntity(value=\" + this.value + \")\";\n        }\n\n    }\n\n    public static class ValueEntityPredicate extends ValuePredicate<ValueEntity> {\n\n        private final Optional<EntityType<? extends Entity>> entityType;\n\n        public ValueEntityPredicate(Optional<EntityType<? extends Entity>> entityType) {\n            super(Optional.of(ValueTypes.OBJECT_ENTITY), Optional.empty(), Optional.empty());\n            this.entityType = entityType;\n        }\n\n        public Optional<EntityType<? extends Entity>> getEntityType() {\n            return entityType;\n        }\n\n        @Override\n        protected boolean testTyped(ValueEntity value) {\n            return super.testTyped(value)\n                    && (entityType.isEmpty()\n                        || (value.getRawValue().isPresent() && value.getRawValue().get().getType() == entityType.get()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeEntityClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.world.entity.Entity;\n\nimport java.util.Optional;\nimport java.util.UUID;\n\n/**\n * @author rubensworks\n */\npublic class ValueObjectTypeEntityClient {\n\n    public static Optional<Entity> getEntity(UUID uuid) {\n        for (Entity entity : Minecraft.getInstance().level.entitiesForRendering()) {\n            if (entity.getUUID().equals(uuid)) {\n                return Optional.of(entity);\n            }\n        }\n        return Optional.empty();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeFluidStack.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeUniquelyNamed;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeItemStackLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\nimport java.util.Objects;\n\n/**\n * Value type with values that are fluidstacks.\n * @author rubensworks\n */\npublic class ValueObjectTypeFluidStack extends ValueObjectTypeBase<ValueObjectTypeFluidStack.ValueFluidStack> implements\n        IValueTypeNamed<ValueObjectTypeFluidStack.ValueFluidStack>,\n        IValueTypeUniquelyNamed<ValueObjectTypeFluidStack.ValueFluidStack>,\n        IValueTypeNullable<ValueObjectTypeFluidStack.ValueFluidStack> {\n\n    public ValueObjectTypeFluidStack() {\n        super(\"fluidstack\", ValueObjectTypeFluidStack.ValueFluidStack.class);\n    }\n\n    @Override\n    public ValueFluidStack getDefault() {\n        return ValueFluidStack.of(FluidStack.EMPTY);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueFluidStack value) {\n        FluidStack fluidStack = value.getRawValue();\n        return !fluidStack.isEmpty() ? fluidStack.getHoverName().copy().append(String.format(\" (%s mB)\", fluidStack.getAmount())) : Component.literal(\"\");\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueFluidStack value) {\n        valueOutput.store(\"v\", FluidStack.OPTIONAL_CODEC, value.getRawValue());\n    }\n\n    @Override\n    public ValueFluidStack deserialize(ValueInput valueInput) {\n        return ValueFluidStack.of(valueInput.read(\"v\", FluidStack.OPTIONAL_CODEC).orElse(FluidStack.EMPTY));\n    }\n\n    @Override\n    public String getName(ValueFluidStack a) {\n        return toCompactString(a).getString();\n    }\n\n    @Override\n    public boolean isNull(ValueFluidStack a) {\n        return a.getRawValue().isEmpty();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeItemStackLPElement<>(this, new ValueTypeItemStackLPElement.IItemStackToValue<ValueObjectTypeFluidStack.ValueFluidStack>() {\n            @Override\n            public boolean isNullable() {\n                return true;\n            }\n\n            @Override\n            public Component validate(ItemStack itemStack) {\n                return itemStack.isEmpty() ||\n                        ItemAccess.forStack(itemStack).getCapability(Capabilities.Fluid.ITEM) != null ?\n                        null :\n                        Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);\n            }\n\n            @Override\n            public ValueObjectTypeFluidStack.ValueFluidStack getValue(ItemStack itemStack) {\n                return ValueObjectTypeFluidStack.ValueFluidStack.of(Helpers.getFluidStack(itemStack));\n            }\n\n            @Override\n            public ItemStack getValueAsItemStack(ValueFluidStack value) {\n                return value.getRawValue().getFluid().getFluidType().getBucket(value.getRawValue());\n            }\n        });\n    }\n\n    @Override\n    public String getUniqueName(ValueFluidStack value) {\n        FluidStack fluidStack = value.getRawValue();\n        return !fluidStack.isEmpty() ?\n                String.format(\"%s %s\", BuiltInRegistries.FLUID.getKey(fluidStack.getFluid()), fluidStack.getAmount()) : \"\";\n    }\n\n    public static class ValueFluidStack extends ValueBase {\n\n        private final FluidStack fluidStack;\n\n        private ValueFluidStack(FluidStack itemStack) {\n            super(ValueTypes.OBJECT_FLUIDSTACK);\n            this.fluidStack = Objects.requireNonNull(itemStack, \"Attempted to create a ValueFluidStack for a null FluidStack.\");\n        }\n\n        public static ValueFluidStack of(FluidStack itemStack) {\n            return new ValueFluidStack(itemStack);\n        }\n\n        public FluidStack getRawValue() {\n            return fluidStack;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueFluidStack && FluidStack.matches(this.getRawValue(), ((ValueFluidStack) o).getRawValue());\n        }\n\n        @Override\n        public int hashCode() {\n            return fluidStack.hashCode();\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueFluidStack(fluidStack=\" + this.fluidStack + \")\";\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeIngredients.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeIngredientsLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\n/**\n * Value type with values that are ingredients.\n * @author rubensworks\n */\npublic class ValueObjectTypeIngredients extends ValueObjectTypeBase<ValueObjectTypeIngredients.ValueIngredients> implements\n        IValueTypeNamed<ValueObjectTypeIngredients.ValueIngredients>, IValueTypeNullable<ValueObjectTypeIngredients.ValueIngredients> {\n\n    public ValueObjectTypeIngredients() {\n        super(\"ingredients\", ValueObjectTypeIngredients.ValueIngredients.class);\n    }\n\n    @Override\n    public ValueIngredients getDefault() {\n        return ValueIngredients.of(null);\n    }\n\n    public static MutableComponent ingredientsToTextComponent(IMixedIngredients ingredients) {\n        MutableComponent sb = Component.literal(\"\");\n\n        for (IngredientComponent<?, ?> component : ingredients.getComponents()) {\n            IIngredientComponentHandler handler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n            for (Object instance : ingredients.getInstances(component)) {\n                if (sb.getSiblings().size() > 0) {\n                    sb.append(Component.literal(\", \"));\n                }\n                sb.append(handler.toCompactString(handler.toValue(instance)));\n            }\n        }\n\n        return sb;\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueIngredients value) {\n        if (value.getRawValue().isPresent()) {\n            return ingredientsToTextComponent(value.getRawValue().get());\n        }\n        return Component.literal(\"\");\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueIngredients value) {\n        value.getRawValue().ifPresent(v -> IMixedIngredients.serialize(valueOutput.child(\"v\"), v));\n    }\n\n    @Override\n    public ValueIngredients deserialize(ValueInput valueInput) {\n        return ValueIngredients.of(valueInput.child(\"v\").map(IMixedIngredients::deserialize).orElse(null));\n    }\n\n    @Override\n    public String getName(ValueIngredients a) {\n        return toCompactString(a).getString();\n    }\n\n    @Override\n    public boolean isNull(ValueIngredients a) {\n        return !a.getRawValue().isPresent();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeIngredientsLPElement();\n    }\n\n    public static class ValueIngredients extends ValueOptionalBase<IMixedIngredients> {\n\n        private ValueIngredients(IMixedIngredients recipe) {\n            super(ValueTypes.OBJECT_INGREDIENTS, recipe);\n        }\n\n        public static ValueIngredients of(IMixedIngredients recipe) {\n            return new ValueIngredients(recipe);\n        }\n\n        @Override\n        protected boolean isEqual(IMixedIngredients a, IMixedIngredients b) {\n            return a.equals(b);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueIngredients()\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeItemStack.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.advancements.criterion.ItemPredicate;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeUniquelyNamed;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeItemStackLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Value type with values that are itemstacks.\n * @author rubensworks\n */\npublic class ValueObjectTypeItemStack extends ValueObjectTypeBase<ValueObjectTypeItemStack.ValueItemStack> implements\n        IValueTypeNamed<ValueObjectTypeItemStack.ValueItemStack>,\n        IValueTypeUniquelyNamed<ValueObjectTypeItemStack.ValueItemStack>,\n        IValueTypeNullable<ValueObjectTypeItemStack.ValueItemStack> {\n\n    public ValueObjectTypeItemStack() {\n        super(\"itemstack\", ValueObjectTypeItemStack.ValueItemStack.class);\n    }\n\n    public static MutableComponent getItemStackDisplayNameUsSafe(ItemStack itemStack) throws NoSuchMethodException {\n        return !itemStack.isEmpty()\n                ? (itemStack.getHoverName().copy().append((itemStack.getCount() > 1 ? \" (\" + itemStack.getCount() + \")\" : \"\")))\n                : Component.literal(\"\");\n    }\n\n    public static MutableComponent getItemStackDisplayNameSafe(ItemStack itemStack) {\n        // Certain mods may call client-side only methods,\n        // so call a server-side-safe fallback method if that fails.\n        try {\n            return getItemStackDisplayNameUsSafe(itemStack);\n        } catch (NoSuchMethodException e) {\n            return Component.translatable(itemStack.getItem().getDescriptionId());\n        }\n    }\n\n    @Override\n    public ValueItemStack getDefault() {\n        return ValueItemStack.of(ItemStack.EMPTY);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueItemStack value) {\n        return ValueObjectTypeItemStack.getItemStackDisplayNameSafe(value.getRawValue());\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueItemStack value) {\n        valueOutput = valueOutput.child(\"v\");\n        ItemStack itemStack;\n        itemStack = value.getRawValue();\n        int count = itemStack.getCount();\n        if (itemStack.getCount() > 99) {\n            itemStack = itemStack.copy();\n            itemStack.setCount(99);\n        }\n        if (count > 99) {\n            valueOutput.putInt(\"ExtendedCount\", count);\n        }\n        valueOutput.store(\"stack\", ItemStack.OPTIONAL_CODEC, itemStack);\n    }\n\n    @Override\n    public ValueItemStack deserialize(ValueInput valueInput) {\n        valueInput = valueInput.child(\"v\").orElseThrow();\n        ItemStack itemStack = valueInput.read(\"stack\", ItemStack.OPTIONAL_CODEC).orElseThrow();\n        valueInput.getInt(\"ExtendedCount\").ifPresent(itemStack::setCount);\n        return ValueItemStack.of(itemStack);\n    }\n\n    @Override\n    public String getName(ValueItemStack a) {\n        return toCompactString(a).getString();\n    }\n\n    @Override\n    public boolean isNull(ValueItemStack a) {\n        return a.getRawValue().isEmpty();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeItemStackLPElement<>(this, new ValueTypeItemStackLPElement.IItemStackToValue<ValueObjectTypeItemStack.ValueItemStack>() {\n            @Override\n            public boolean isNullable() {\n                return true;\n            }\n\n            @Override\n            public Component validate(ItemStack itemStack) {\n                return null;\n            }\n\n            @Override\n            public ValueObjectTypeItemStack.ValueItemStack getValue(ItemStack itemStack) {\n                return ValueObjectTypeItemStack.ValueItemStack.of(itemStack);\n            }\n\n            @Override\n            public ItemStack getValueAsItemStack(ValueItemStack value) {\n                return value.getRawValue();\n            }\n        });\n    }\n\n    @Override\n    public ValueItemStack materialize(ValueItemStack value) throws EvaluationException {\n        return ValueItemStack.of(value.getRawValue().copy());\n    }\n\n    @Override\n    public String getUniqueName(ValueItemStack value) {\n        ItemStack itemStack = value.getRawValue();\n        return !itemStack.isEmpty() ? BuiltInRegistries.ITEM.getKey(itemStack.getItem()).toString() : \"\";\n    }\n\n    public static class ValueItemStack extends ValueBase {\n\n        private final ItemStack itemStack;\n\n        private ValueItemStack(ItemStack itemStack) {\n            super(ValueTypes.OBJECT_ITEMSTACK);\n            this.itemStack = Objects.requireNonNull(itemStack, \"Attempted to create a ValueItemStack for a null ItemStack.\");\n        }\n\n        public static ValueItemStack of(ItemStack itemStack) {\n            return new ValueItemStack(itemStack);\n        }\n\n        public ItemStack getRawValue() {\n            return itemStack;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueItemStack && ItemMatch.areItemStacksEqual(((ValueItemStack) o).itemStack, this.itemStack, ItemMatch.EXACT);\n        }\n\n        @Override\n        public int hashCode() {\n            return 37 + IModHelpers.get().getItemStackHelpers().getItemStackHashCode(itemStack);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueItemStack(itemStack=\" + this.itemStack + \")\";\n        }\n    }\n\n    public static class ValueItemStackPredicate extends ValuePredicate<ValueItemStack> {\n\n        private final Optional<ItemPredicate> itemPredicate;\n\n        public ValueItemStackPredicate(Optional<ItemPredicate> itemPredicate) {\n            super(Optional.of(ValueTypes.OBJECT_ITEMSTACK), Optional.empty(), Optional.empty());\n            this.itemPredicate = itemPredicate;\n        }\n\n        public Optional<ItemPredicate> getItemPredicate() {\n            return itemPredicate;\n        }\n\n        @Override\n        protected boolean testTyped(ValueItemStack value) {\n            return super.testTyped(value) && (itemPredicate.isEmpty() || itemPredicate.get().test(value.getRawValue()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeRecipe.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Iterables;\nimport com.google.gson.JsonParseException;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\n\n/**\n * Value type with values that are recipes.\n * @author rubensworks\n */\npublic class ValueObjectTypeRecipe extends ValueObjectTypeBase<ValueObjectTypeRecipe.ValueRecipe> implements\n        IValueTypeNamed<ValueObjectTypeRecipe.ValueRecipe>, IValueTypeNullable<ValueObjectTypeRecipe.ValueRecipe> {\n\n    public ValueObjectTypeRecipe() {\n        super(\"recipe\", ValueObjectTypeRecipe.ValueRecipe.class);\n    }\n\n    @Override\n    protected ValueObjectTypeRecipeClient constructClient() {\n        return new ValueObjectTypeRecipeClient(this);\n    }\n\n    @Override\n    public ValueRecipe getDefault() {\n        return ValueRecipe.of(null);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueRecipe value) {\n        if (value.getRawValue().isPresent()) {\n            IRecipeDefinition recipe = value.getRawValue().get();\n            MutableComponent sb = Component.literal(\"\");\n\n            sb.append(ValueObjectTypeIngredients.ingredientsToTextComponent(recipe.getOutput()));\n            sb.append(Component.literal(\" <- \"));\n            boolean first = true;\n\n            for (IngredientComponent<?, ?> component : recipe.getInputComponents()) {\n                IIngredientComponentHandler handler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n                int i = 0;\n                for (IPrototypedIngredientAlternatives<?, ?> instances : recipe.getInputs(component)) {\n                    IPrototypedIngredient<?, ?> prototypedIngredient = Iterables.getFirst(instances.getAlternatives(), null);\n                    IValue v;\n                    if (prototypedIngredient == null) {\n                        v  = handler.getValueType().getDefault();\n                    } else {\n                        v = handler.toValue(prototypedIngredient.getPrototype());\n                    }\n                    if (!first) {\n                        sb.append(Component.literal(\", \"));\n                    } else {\n                        first = false;\n                    }\n                    sb.append(handler.toCompactString(v));\n                    if (recipe.isInputReusable(component, i)) {\n                        sb.append(\"*\");\n                    }\n                    i++;\n                }\n            }\n            return sb;\n        }\n        return Component.literal(\"\");\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueRecipe value) {\n        value.getRawValue().ifPresent(v -> IRecipeDefinition.serialize(valueOutput.child(\"v\"), v));\n    }\n\n    @Override\n    public ValueRecipe deserialize(ValueInput valueInput) {\n        try {\n            return ValueRecipe.of(valueInput.child(\"v\").map(IRecipeDefinition::deserialize).orElse(null));\n        } catch (IllegalArgumentException | JsonParseException e) {\n            return ValueRecipe.of(null);\n        }\n    }\n\n    @Override\n    public String getName(ValueRecipe a) {\n        return toCompactString(a).getString();\n    }\n\n    @Override\n    public boolean isNull(ValueRecipe a) {\n        return !a.getRawValue().isPresent();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeRecipeLPElement();\n    }\n\n    public static class ValueRecipe extends ValueOptionalBase<IRecipeDefinition> {\n\n        private ValueRecipe(IRecipeDefinition recipe) {\n            super(ValueTypes.OBJECT_RECIPE, recipe);\n        }\n\n        public static ValueRecipe of(IRecipeDefinition recipe) {\n            return new ValueRecipe(recipe);\n        }\n\n        @Override\n        protected boolean isEqual(IRecipeDefinition a, IRecipeDefinition b) {\n            return a.equals(b);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueRecipe()\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueObjectTypeRecipeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.item.ItemStackRenderState;\nimport net.minecraft.client.renderer.texture.OverlayTexture;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic class ValueObjectTypeRecipeClient extends ValueTypeBaseClient<ValueObjectTypeRecipe.ValueRecipe> {\n\n    public ValueObjectTypeRecipeClient(ValueTypeBase<ValueObjectTypeRecipe.ValueRecipe> valueType) {\n        super(valueType);\n    }\n\n    @Override\n    public void renderISTER(ValueObjectTypeRecipe.ValueRecipe value, ItemStack stack, ItemDisplayContext transformType, PoseStack matrixStack, SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay) {\n        if (IModHelpers.get().getMinecraftClientHelpers().isShifted()) {\n            value.getRawValue()\n                    .ifPresent((recipe) -> {\n                        List<ItemStack> itemStacks = recipe.getOutput().getInstances(IngredientComponent.ITEMSTACK);\n                        if (!itemStacks.isEmpty()) {\n                            ItemStack actualStack = itemStacks.get(0);\n                            ItemStackRenderState renderState = new ItemStackRenderState();\n                            matrixStack.pushPose();\n                            matrixStack.translate(0.03F, 0F, 0F);\n                            matrixStack.translate(0F, 0F, -0.15F);\n                            matrixStack.scale(0.8F, 0.8F, 0.01F);\n                            Minecraft.getInstance().getItemModelResolver().updateForTopItem(renderState, actualStack, ItemDisplayContext.GUI, Minecraft.getInstance().level, null, 0);\n                            renderState.submit(matrixStack, submitNodeCollector, 15728880, OverlayTexture.NO_OVERLAY, 0);\n                            matrixStack.popPose();\n                        }\n                    });\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueOptionalBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Base value implementation for nullable values.\n * @author rubensworks\n */\npublic abstract class ValueOptionalBase<T> extends ValueBase {\n\n    private final Optional<T> value;\n\n    public ValueOptionalBase(IValueType type, @Nullable T value) {\n        super(type);\n        this.value = Optional.ofNullable(preprocessValue(value));\n    }\n\n    @Nullable\n    protected T preprocessValue(@Nullable T value) {\n        return value;\n    }\n\n    /**\n     * @return The raw value in an optional holder.\n     */\n    public Optional<T> getRawValue() {\n        return value;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public boolean equals(Object o) {\n        if((o instanceof ValueOptionalBase) && getType() == ((ValueOptionalBase) o).getType()) {\n            if (((ValueOptionalBase) o).getRawValue().isPresent() && getRawValue().isPresent()) {\n                return isEqual(((ValueOptionalBase<T>) o).getRawValue().get(), getRawValue().get());\n            } else if (!((ValueOptionalBase) o).getRawValue().isPresent() && !getRawValue().isPresent()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    protected abstract boolean isEqual(T a, T b);\n\n    @Override\n    public int hashCode() {\n        return getType().hashCode() + (getRawValue().isPresent() ? getRawValue().get().hashCode() : 0);\n    }\n\n    @Override\n    public String toString() {\n        return getRawValue().toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeStringLPElement;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Consumer;\n\n/**\n * Base implementation of a value type.\n * @author rubensworks\n */\npublic abstract class ValueTypeBase<V extends IValue> implements IValueType<V> {\n\n    private final String typeName;\n    private final int color;\n    private final ChatFormatting colorFormat;\n    private final Class<V> valueClass;\n    private ValueTypeBaseClient<V> client;\n\n    private String translationKey = null;\n\n    public ValueTypeBase(String typeName, int color, ChatFormatting colorFormat, Class<V> valueClass) {\n        this.typeName = typeName;\n        this.color = color;\n        this.colorFormat = colorFormat;\n        this.valueClass = valueClass;\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment() && IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            this.client = constructClient();\n            this.client.registerModelIdentifier();\n        }\n    }\n\n    protected ValueTypeBaseClient<V> constructClient() {\n        return new ValueTypeBaseClient<>(this);\n    }\n\n    @Override\n    public ValueTypeBaseClient<V> getClient() {\n        return client;\n    }\n\n    @Override\n    public boolean isCategory() {\n        return false;\n    }\n\n    @Override\n    public boolean isObject() {\n        return false;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(getModId(), getTypeName());\n    }\n\n    protected String getUnlocalizedPrefix() {\n        return \"valuetype.\" + getModId() + getTypeNamespace() + getTypeName();\n    }\n\n    protected String getTypeNamespace() {\n        return \".\";\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return translationKey != null ? translationKey : (translationKey = getUnlocalizedPrefix());\n    }\n\n    @Override\n    public String getTypeName() {\n        return this.typeName;\n    }\n\n    @Override\n    public int getDisplayColor() {\n        return this.color;\n    }\n\n    @Override\n    public ChatFormatting getDisplayColorFormat() {\n        return this.colorFormat;\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType<?> valueType) {\n        return this == valueType;\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo, @Nullable V value) {\n        String typeName = IModHelpers.get().getL10NHelpers().localize(getTranslationKey());\n        tooltipAdder.accept(Component.translatable(L10NValues.VALUETYPE_TOOLTIP_TYPENAME, getDisplayColorFormat() + typeName));\n        if(appendOptionalInfo) {\n            IModHelpers.get().getL10NHelpers().addOptionalInfo(tooltipAdder, getUnlocalizedPrefix(), net.minecraft.world.item.TooltipFlag.NORMAL);\n        }\n    }\n\n    @Override\n    public Component canDeserialize(ValueInput valueInput) {\n        try {\n            deserialize(valueInput);\n            return null;\n        } catch (IllegalArgumentException e) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, valueInput);\n        }\n    }\n\n    @Override\n    public V materialize(V value) throws EvaluationException {\n        return value;\n    }\n\n    @Override\n    public V parseString(String value) throws EvaluationException {\n        throw new UnsupportedOperationException(\"parseString is not supported on value type \" + this);\n    }\n\n    @Override\n    public String toString(V value) {\n        throw new UnsupportedOperationException(\"toString is not supported on value type \" + this);\n    }\n\n    @Override\n    public String toString() {\n        return IModHelpers.get().getL10NHelpers().localize(getTranslationKey());\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeStringLPElement(this);\n    }\n\n    protected String getModId() {\n        return Reference.MOD_ID;\n    }\n\n    @Override\n    public V cast(IValue value) throws EvaluationException {\n        try {\n            return this.valueClass.cast(value);\n        } catch (ClassCastException e) {\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_CAST_ILLEGAL,\n                    Component.translatable(value.getType().getTranslationKey()),\n                    Component.translatable(this.getTranslationKey()),\n                    value.getType().toCompactString(value)\n            ));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeBaseClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeClient;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeBaseClient<V extends IValue> implements IValueTypeClient<V> {\n\n    private final ValueTypeBase<V> valueType;\n\n    public ValueTypeBaseClient(ValueTypeBase<V> valueType) {\n        this.valueType = valueType;\n    }\n\n    public ValueTypeBase<V> getValueType() {\n        return valueType;\n    }\n\n    protected void registerModelIdentifier() {\n        ValueTypes.REGISTRY.getClient().registerValueTypeModel(getValueType(),\n                Identifier.parse(getValueType().getModId() + \":valuetype\" + getValueType().getTypeNamespace().replace('.', '/') + getValueType().getTypeName().replace('.', '/')));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeBoolean.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeBooleanLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\n/**\n * Value type with values 'true' or 'false'\n * @author rubensworks\n */\npublic class ValueTypeBoolean extends ValueTypeBase<ValueTypeBoolean.ValueBoolean> {\n\n    public ValueTypeBoolean() {\n        super(\"boolean\", IModHelpers.get().getBaseHelpers().RGBToInt(43, 47, 231), ChatFormatting.BLUE, ValueTypeBoolean.ValueBoolean.class);\n    }\n\n    @Override\n    public ValueBoolean getDefault() {\n        return ValueBoolean.of(false);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueBoolean value) {\n        return Component.literal(Boolean.toString(value.getRawValue()));\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueBoolean value) {\n        valueOutput.putBoolean(\"v\", value.getRawValue());\n    }\n\n    @Override\n    public ValueBoolean deserialize(ValueInput valueInput) {\n        return ValueBoolean.of(valueInput.getBooleanOr(\"v\", false));\n    }\n\n    @Override\n    public String toString(ValueBoolean value) {\n        return Boolean.toString(value.getRawValue());\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeBooleanLPElement(this);\n    }\n\n    @Override\n    public ValueBoolean parseString(String value) throws EvaluationException {\n        boolean b;\n        if(\"true\".equalsIgnoreCase(value) || \"1\".equals(value)) {\n            b = true;\n        } else if(\"false\".equalsIgnoreCase(value) || \"0\".equals(value)) {\n            b = false;\n        } else {\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value,\n                    Component.translatable(getTranslationKey())));\n        }\n        return ValueBoolean.of(b);\n    }\n\n    public static class ValueBoolean extends ValueBase {\n\n        private static final ValueBoolean TRUE = new ValueBoolean(true);\n        private static final ValueBoolean FALSE = new ValueBoolean(false);\n\n        private final boolean value;\n\n        private ValueBoolean(boolean value) {\n            super(ValueTypes.BOOLEAN);\n            this.value = value;\n        }\n\n        public static ValueBoolean of(boolean value) {\n            return value ? TRUE : FALSE;\n        }\n\n        public boolean getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueBoolean && ((ValueBoolean) o).value == this.value;\n        }\n\n        @Override\n        public int hashCode() {\n            return getType().hashCode() + (value ? 1 : 0);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueBoolean(value=\" + this.value + \")\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeCategoryAny.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\n\n/**\n * Wildcard value type\n * @author rubensworks\n */\npublic class ValueTypeCategoryAny extends ValueTypeCategoryBase<IValue> {\n\n    public ValueTypeCategoryAny() {\n        super(\"any\", IModHelpers.get().getBaseHelpers().RGBToInt(240, 240, 240), ChatFormatting.RESET, IValue.class);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeCategoryBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeCategory;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * Base implementation of a value type category.\n * @author rubensworks\n */\npublic abstract class ValueTypeCategoryBase<V extends IValue> extends ValueTypeBase<V> implements IValueTypeCategory<V> {\n\n    @Nullable\n    protected final Set<IValueType<?>> elements;\n\n    /**\n     * Make a new instance.\n     * @param typeName The category name.\n     * @param color The color.\n     * @param colorFormat The color format.\n     * @param elements The elements inside this category.\n     * @param valueClass The value type class.\n     */\n    public ValueTypeCategoryBase(String typeName, int color, ChatFormatting colorFormat, Set<IValueType<?>> elements, Class<V> valueClass) {\n        super(typeName, color, colorFormat, valueClass);\n        this.elements = Collections.unmodifiableSet(elements);\n    }\n\n    /**\n     * Make a new instance.\n     * @param typeName The category name.\n     * @param color The color.\n     * @param colorFormat The color format.\n     * @param valueClass The value type class.\n     */\n    public ValueTypeCategoryBase(String typeName, int color, ChatFormatting colorFormat, Class<V> valueClass) {\n        super(typeName, color, colorFormat, valueClass);\n        this.elements = null;\n    }\n\n    @Override\n    public boolean isCategory() {\n        return true;\n    }\n\n    @Override\n    public V getDefault() {\n        // Avoid crashes when default values would be used somewhere.\n        return (V) ValueTypeBoolean.ValueBoolean.of(false);\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return getUnlocalizedPrefix();\n    }\n\n    @Override\n    public MutableComponent toCompactString(V value) {\n        return null;\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType<?> valueType) {\n        return valueType == this || elements == null ? true : elements.contains(valueType);\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, V value) {\n        throw new UnsupportedOperationException(\"This operation is not allowed\");\n    }\n\n    @Override\n    public V deserialize(ValueInput valueInput) {\n        throw new UnsupportedOperationException(\"This operation is not allowed\");\n    }\n\n    protected String getModId() {\n        return Reference.MOD_ID;\n    }\n\n    @Override\n    public Set<IValueType<?>> getElements() {\n        return elements == null ? Collections.emptySet() : Collections.unmodifiableSet(elements);\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeCategoryNamed.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\n/**\n * Value type category with values that have a name.\n * @author rubensworks\n */\npublic class ValueTypeCategoryNamed extends ValueTypeCategoryBase<IValue> {\n\n    public ValueTypeCategoryNamed() {\n        super(\"named\", IModHelpers.get().getBaseHelpers().RGBToInt(250, 10, 13), ChatFormatting.RED, IValue.class);\n    }\n\n    public String getName(IVariable a) throws EvaluationException {\n        IValueTypeNamed<IValue> type = ValueHelpers.variableUnpackAnyType(a, Operators.NAMED_NAME, this, IValueTypeNamed.class);\n        return type.getName(a.getValue());\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType<?> valueType) {\n        return super.correspondsTo(valueType) && valueType instanceof IValueTypeNamed;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeCategoryNullable.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\n/**\n * Value type category with values that can be null.\n * @author rubensworks\n */\npublic class ValueTypeCategoryNullable extends ValueTypeCategoryBase<IValue> {\n\n    public ValueTypeCategoryNullable() {\n        super(\"nullable\", IModHelpers.get().getBaseHelpers().RGBToInt(100, 100, 100), ChatFormatting.DARK_GRAY, IValue.class);\n    }\n\n    public boolean isNull(IVariable a) throws EvaluationException {\n        IValueTypeNullable<IValue> type = ValueHelpers.variableUnpackAnyType(a, Operators.NULLABLE_ISNULL, this, IValueTypeNullable.class);\n        return type.isNull(a.getValue());\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType<?> valueType) {\n        return super.correspondsTo(valueType) && valueType instanceof IValueTypeNullable;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeCategoryNumber.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueCastRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNumber;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * Value type category with values that are numbers.\n * @author rubensworks\n */\npublic class ValueTypeCategoryNumber extends ValueTypeCategoryBase<IValue> implements IValueTypeNamed<IValue> {\n\n    private static final IValueTypeNumber[] ELEMENTS = new IValueTypeNumber[]{ValueTypes.INTEGER, ValueTypes.LONG, ValueTypes.DOUBLE};\n    private static final Map<IValueTypeNumber, Integer> INVERTED_ELEMENTS = Collections.unmodifiableMap(constructInvertedArray(ELEMENTS));\n\n    public ValueTypeCategoryNumber() {\n        super(\"number\", IModHelpers.get().getBaseHelpers().RGBToInt(243, 245, 4), ChatFormatting.GOLD,\n                Sets.<IValueType<?>>newHashSet(ELEMENTS), IValue.class);\n    }\n\n    private static Map<IValueTypeNumber, Integer> constructInvertedArray(IValueTypeNumber[] elements) {\n        Map<IValueTypeNumber, Integer> map = Maps.newHashMap();\n        for(int i = 0; i < elements.length; i++) {\n            map.put(elements[i], i);\n        }\n        return map;\n    }\n\n    public IValueTypeNumber getLowestType(IValueTypeNumber... types) {\n        IValueTypeNumber first = types[0];\n        for(int i = 1; i < types.length; i++) {\n            if(types[i] != first) {\n                int maxIndex = -1;\n                for(int j = 0; j < types.length; j++) {\n                    IValueTypeNumber v = types[j];\n                    if(v != null) {\n                        maxIndex = Math.max(maxIndex, INVERTED_ELEMENTS.get(v));\n                    }\n                }\n                return ELEMENTS[maxIndex];\n            }\n        }\n        return first;\n    }\n\n    protected IValue castValue(IValueTypeNumber type, IValue value) throws IValueCastRegistry.ValueCastException {\n        if(value.getType() == type) {\n            return value;\n        } else {\n            return ValueCastMappings.REGISTRY.cast(type, value);\n        }\n    }\n\n    protected IValueTypeNumber getType(IVariable v) throws EvaluationException {\n        IValueType valueType = v.getType();\n        // Special case if the type is ANY.\n        if (!(valueType instanceof IValueTypeNumber)) {\n            // Don't evaluate the var if not needed,\n            // as this may be expensive.\n            valueType = v.getValue().getType();\n        }\n        return ((IValueTypeNumber) valueType);\n    }\n\n    public IValue add(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        IValue av = castValue(type, a.getValue());\n        if (type.isZero(av)) { // If a is neutral element for addition\n            return castValue(type, b.getValue());\n        } else {\n            IValue bv = castValue(type, b.getValue());\n            if (type.isZero(bv)) { // If b is neutral element for addition\n                return av;\n            } else {\n                return type.add(av, bv);\n            }\n        }\n    }\n\n    public IValue subtract(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        IValue bv = castValue(type, b.getValue());\n        if (type.isZero(bv)) { // If b is neutral element for subtraction\n            return castValue(type, a.getValue());\n        } else {\n            IValue av = castValue(type, a.getValue());\n            return type.subtract(av, bv);\n        }\n    }\n\n    public IValue multiply(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        IValue av = castValue(type, a.getValue());\n        if (type.isZero(av)) { // If a is absorbtion element for multiplication\n            return av;\n        } else if (type.isOne(av)) { // If a is neutral element for multiplication\n            return castValue(type, b.getValue());\n        } else {\n            IValue bv = castValue(type, b.getValue());\n            if (type.isOne(bv)) { // If b is neutral element for multiplication\n                return av;\n            } else {\n                return type.multiply(av, bv);\n            }\n        }\n    }\n\n    public IValue divide(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        IValue bv = castValue(type, b.getValue());\n        if (type.isZero(bv)) { // You can not divide by zero\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_DIVIDEBYZERO));\n        } else if (type.isOne(bv)) { // If b is neutral element for division\n            return a.getValue();\n        } else {\n            IValue av = castValue(type, a.getValue());\n            return type.divide(av, bv);\n        }\n    }\n\n    public IValue max(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        return type.max(\n                castValue(type, a.getValue()),\n                castValue(type, b.getValue())\n        );\n    }\n\n    public IValue min(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        return type.min(\n                castValue(type, a.getValue()),\n                castValue(type, b.getValue())\n        );\n    }\n\n    public IValue increment(IVariable a) throws EvaluationException {\n        IValueTypeNumber type = getType(a);\n        return type.increment(castValue(type, a.getValue()));\n    }\n\n    public IValue decrement(IVariable a) throws EvaluationException {\n        IValueTypeNumber type = getType(a);\n        return type.decrement(castValue(type, a.getValue()));\n    }\n\n    public IValue modulus(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        IValue bValue = castValue(type, b.getValue());\n        if (type.isZero(bValue)) { // You can not divide by zero\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_DIVIDEBYZERO));\n        } else if (type.isOne(bValue)) { // If b is neutral element for division\n            return type.getDefault();\n        } else {\n            return type.modulus(\n                castValue(type, a.getValue()),\n                bValue\n            );\n        }\n    }\n\n    public boolean greaterThan(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        return type.greaterThan(\n                castValue(type, a.getValue()),\n                castValue(type, b.getValue())\n        );\n    }\n\n    public boolean lessThan(IVariable a, IVariable b) throws EvaluationException {\n        IValueTypeNumber type = getLowestType(getType(a), getType(b));\n        return type.lessThan(\n                castValue(type, a.getValue()),\n                castValue(type, b.getValue())\n        );\n    }\n\n    public ValueTypeInteger.ValueInteger round(IVariable a) throws EvaluationException {\n        IValueTypeNumber type = getType(a);\n        return type.round(castValue(type, a.getValue()));\n    }\n\n    public ValueTypeInteger.ValueInteger ceil(IVariable a) throws EvaluationException {\n        IValueTypeNumber type = getType(a);\n        return type.ceil(castValue(type, a.getValue()));\n    }\n\n    public ValueTypeInteger.ValueInteger floor(IVariable a) throws EvaluationException {\n        IValueTypeNumber type = getType(a);\n        return type.floor(castValue(type, a.getValue()));\n    }\n\n    public ValueTypeString.ValueString compact(IVariable a) throws EvaluationException {\n        IValueTypeNumber type = getType(a);\n        return type.compact(castValue(type, a.getValue()));\n    }\n\n    @Override\n    public String getName(IValue a) {\n        return ((IValueTypeNamed) a.getType()).getName(a);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeCategoryUniquelyNamed.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeUniquelyNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\n/**\n * Value type category with values that have a unique name.\n * @author rubensworks\n */\npublic class ValueTypeCategoryUniquelyNamed extends ValueTypeCategoryBase<IValue> {\n\n    public ValueTypeCategoryUniquelyNamed() {\n        super(\"uniquely_named\", IModHelpers.get().getBaseHelpers().RGBToInt(250, 10, 13), ChatFormatting.RED, IValue.class);\n    }\n\n    public String getUniqueName(IVariable a) throws EvaluationException {\n        IValueTypeUniquelyNamed<IValue> type = ValueHelpers.variableUnpackAnyType(a, Operators.UNIQUELYNAMED_UNIQUENAME, this, IValueTypeUniquelyNamed.class);\n        return type.getUniqueName(a.getValue());\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType<?> valueType) {\n        return super.correspondsTo(valueType) && valueType instanceof IValueTypeUniquelyNamed;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeDouble.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNumber;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.text.NumberFormat;\nimport java.util.Locale;\n\n/**\n * Value type with values that are doubles.\n * @author rubensworks\n */\npublic class ValueTypeDouble extends ValueTypeBase<ValueTypeDouble.ValueDouble> implements IValueTypeNumber<ValueTypeDouble.ValueDouble> {\n\n    public ValueTypeDouble() {\n        super(\"double\", IModHelpers.get().getBaseHelpers().RGBToInt(235, 234, 23), ChatFormatting.YELLOW, ValueTypeDouble.ValueDouble.class);\n    }\n\n    @Override\n    public ValueDouble getDefault() {\n        return ValueDouble.of(0D);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueDouble value) {\n        return Component.literal(Double.toString(value.getRawValue()));\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueDouble value) {\n        valueOutput.putDouble(\"v\", value.getRawValue());\n    }\n\n    @Override\n    public ValueDouble deserialize(ValueInput valueInput) {\n        return ValueDouble.of(valueInput.getDoubleOr(\"v\", 0));\n    }\n\n    @Override\n    public String toString(ValueDouble value) {\n        return Double.toString(value.getRawValue());\n    }\n\n    @Override\n    public ValueDouble parseString(String value) throws EvaluationException {\n        try {\n            return ValueDouble.of(Double.parseDouble(value));\n        } catch (NumberFormatException e) {\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value,\n                    Component.translatable(getTranslationKey())));\n        }\n    }\n\n    @Override\n    public boolean isZero(ValueDouble a) {\n        return a.getRawValue() == 0D;\n    }\n\n    @Override\n    public boolean isOne(ValueDouble a) {\n        return a.getRawValue() == 1D;\n    }\n\n    @Override\n    public ValueDouble add(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(a.getRawValue() + b.getRawValue());\n    }\n\n    @Override\n    public ValueDouble subtract(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(a.getRawValue() - b.getRawValue());\n    }\n\n    @Override\n    public ValueDouble multiply(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(a.getRawValue() * b.getRawValue());\n    }\n\n    @Override\n    public ValueDouble divide(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(a.getRawValue() / b.getRawValue());\n    }\n\n    @Override\n    public ValueDouble max(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(Math.max(a.getRawValue(), b.getRawValue()));\n    }\n\n    @Override\n    public ValueDouble min(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(Math.min(a.getRawValue(), b.getRawValue()));\n    }\n\n    @Override\n    public ValueDouble increment(ValueDouble a) {\n        return ValueDouble.of(a.getRawValue() + 1D);\n    }\n\n    @Override\n    public ValueDouble decrement(ValueDouble a) {\n        return ValueDouble.of(a.getRawValue() - 1D);\n    }\n\n    @Override\n    public ValueDouble modulus(ValueDouble a, ValueDouble b) {\n        return ValueDouble.of(a.getRawValue() % b.getRawValue());\n    }\n\n    @Override\n    public boolean greaterThan(ValueDouble a, ValueDouble b) {\n        return a.getRawValue() > b.getRawValue();\n    }\n\n    @Override\n    public boolean lessThan(ValueDouble a, ValueDouble b) {\n        return a.getRawValue() < b.getRawValue();\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger round(ValueDouble a) {\n        return ValueTypeInteger.ValueInteger.of((int) Math.round(a.getRawValue()));\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger ceil(ValueDouble a) {\n        return ValueTypeInteger.ValueInteger.of((int) Math.ceil(a.getRawValue()));\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger floor(ValueDouble a) {\n        return ValueTypeInteger.ValueInteger.of((int) Math.floor(a.getRawValue()));\n    }\n\n    @Override\n    public ValueTypeString.ValueString compact(ValueDouble a) {\n        NumberFormat nf = NumberFormat.getCompactNumberInstance(\n            Locale.US,\n            GeneralConfig.numberCompactUseLongStyle ? NumberFormat.Style.LONG : NumberFormat.Style.SHORT\n        );\n        nf.setMinimumFractionDigits(GeneralConfig.numberCompactMinimumFractionDigits);\n        nf.setMaximumFractionDigits(GeneralConfig.numberCompactMaximumFractionDigits);\n        nf.setMinimumIntegerDigits(GeneralConfig.numberCompactMinimumIntegerDigits);\n        nf.setMaximumIntegerDigits(GeneralConfig.numberCompactMaximumIntegerDigits);\n        return ValueTypeString.ValueString.of(nf.format(a.getRawValue()));\n    }\n\n    @Override\n    public String getName(ValueDouble a) {\n        return toCompactString(a).getString();\n    }\n\n    public static class ValueDouble extends ValueBase {\n\n        private final double value;\n\n        private ValueDouble(double value) {\n            super(ValueTypes.DOUBLE);\n            this.value = value;\n        }\n\n        public static ValueDouble of(double value) {\n            return new ValueDouble(value);\n        }\n\n        public double getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueDouble && ((ValueDouble) o).value == this.value;\n        }\n\n        @Override\n        public int hashCode() {\n            return getType().hashCode() + ((int) value * 100);\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueDouble(value=\" + this.value + \")\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeInteger.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNumber;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.text.NumberFormat;\nimport java.util.Locale;\n\n/**\n * Value type with values that are integers.\n * The raw value is nullable.\n * @author rubensworks\n */\npublic class ValueTypeInteger extends ValueTypeBase<ValueTypeInteger.ValueInteger> implements IValueTypeNumber<ValueTypeInteger.ValueInteger> {\n\n    public ValueTypeInteger() {\n        super(\"integer\", IModHelpers.get().getBaseHelpers().RGBToInt(243, 150, 4), ChatFormatting.GOLD, ValueTypeInteger.ValueInteger.class);\n    }\n\n    @Override\n    public ValueInteger getDefault() {\n        return ValueInteger.of(0);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueInteger value) {\n        return Component.literal(Integer.toString(value.getRawValue()));\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueInteger value) {\n        valueOutput.putInt(\"v\", value.getRawValue());\n    }\n\n    @Override\n    public ValueInteger deserialize(ValueInput valueInput) {\n        return ValueInteger.of(valueInput.getInt(\"v\").orElseThrow());\n    }\n\n    @Override\n    public String toString(ValueInteger value) {\n        return Integer.toString(value.getRawValue());\n    }\n\n    @Override\n    public ValueInteger parseString(String value) throws EvaluationException {\n        try {\n            return ValueInteger.of(Integer.parseInt(value));\n        } catch (NumberFormatException e) {\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value,\n                    Component.translatable(getTranslationKey())));\n        }\n    }\n\n    @Override\n    public boolean isZero(ValueInteger a) {\n        return a.getRawValue() == 0;\n    }\n\n    @Override\n    public boolean isOne(ValueInteger a) {\n        return a.getRawValue() == 1;\n    }\n\n    @Override\n    public ValueInteger add(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(a.getRawValue() + b.getRawValue());\n    }\n\n    @Override\n    public ValueInteger subtract(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(a.getRawValue() - b.getRawValue());\n    }\n\n    @Override\n    public ValueInteger multiply(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(a.getRawValue() * b.getRawValue());\n    }\n\n    @Override\n    public ValueInteger divide(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(a.getRawValue() / b.getRawValue());\n    }\n\n    @Override\n    public ValueInteger max(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(Math.max(a.getRawValue(), b.getRawValue()));\n    }\n\n    @Override\n    public ValueInteger min(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(Math.min(a.getRawValue(), b.getRawValue()));\n    }\n\n    @Override\n    public ValueInteger increment(ValueInteger a) {\n        return ValueInteger.of(a.getRawValue() + 1);\n    }\n\n    @Override\n    public ValueInteger decrement(ValueInteger a) {\n        return ValueInteger.of(a.getRawValue() - 1);\n    }\n\n    @Override\n    public ValueInteger modulus(ValueInteger a, ValueInteger b) {\n        return ValueInteger.of(a.getRawValue() % b.getRawValue());\n    }\n\n    @Override\n    public boolean greaterThan(ValueInteger a, ValueInteger b) {\n        return a.getRawValue() > b.getRawValue();\n    }\n\n    @Override\n    public boolean lessThan(ValueInteger a, ValueInteger b) {\n        return a.getRawValue() < b.getRawValue();\n    }\n\n    @Override\n    public ValueInteger round(ValueInteger a) {\n        return a;\n    }\n\n    @Override\n    public ValueInteger ceil(ValueInteger a) {\n        return a;\n    }\n\n    @Override\n    public ValueInteger floor(ValueInteger a) {\n        return a;\n    }\n\n    @Override\n    public ValueTypeString.ValueString compact(ValueInteger a) {\n        NumberFormat nf = NumberFormat.getCompactNumberInstance(\n            Locale.US,\n            GeneralConfig.numberCompactUseLongStyle ? NumberFormat.Style.LONG : NumberFormat.Style.SHORT\n        );\n        nf.setMinimumFractionDigits(GeneralConfig.numberCompactMinimumFractionDigits);\n        nf.setMaximumFractionDigits(GeneralConfig.numberCompactMaximumFractionDigits);\n        nf.setMinimumIntegerDigits(GeneralConfig.numberCompactMinimumIntegerDigits);\n        nf.setMaximumIntegerDigits(GeneralConfig.numberCompactMaximumIntegerDigits);\n        return ValueTypeString.ValueString.of(nf.format(a.getRawValue()));\n    }\n\n    @Override\n    public String getName(ValueInteger a) {\n        return toCompactString(a).getString();\n    }\n\n    public static class ValueInteger extends ValueBase {\n\n        private final int value;\n\n        private ValueInteger(int value) {\n            super(ValueTypes.INTEGER);\n            this.value = value;\n        }\n\n        public static ValueInteger of(int value) {\n            return new ValueInteger(value);\n        }\n\n        public int getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueInteger && ((ValueInteger) o).value == this.value;\n        }\n\n        @Override\n        public int hashCode() {\n            return getType().hashCode() + value;\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueInteger(value=\" + this.value + \")\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeLightLevelRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.integrateddynamics.api.evaluate.InvalidValueTypeException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeLightLevelRegistry;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeLightLevelRegistry implements IValueTypeLightLevelRegistry {\n\n    private static ValueTypeLightLevelRegistry INSTANCE = new ValueTypeLightLevelRegistry();\n\n    private final Map<IValueType<?>, ILightLevelCalculator> lightLevelCalculatorMap = Maps.newHashMap();\n\n    private ValueTypeLightLevelRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static ValueTypeLightLevelRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <L extends ILightLevelCalculator<V>, V extends IValue> L register(IValueType<V> valueType, L lightLevelCalculator) {\n        lightLevelCalculatorMap.put(valueType, lightLevelCalculator);\n        return lightLevelCalculator;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Nullable\n    @Override\n    public <V extends IValue> ILightLevelCalculator<V> getLightLevelCalculator(IValueType<V> valueType) {\n        return lightLevelCalculatorMap.get(valueType);\n    }\n\n    @Override\n    public <V extends IValue> int getLightLevel(V value) throws InvalidValueTypeException {\n        IValueType<V> valueType = value.getType();\n        ILightLevelCalculator<V> lightLevelCalculator = getLightLevelCalculator(valueType);\n        if(lightLevelCalculator != null) {\n            return lightLevelCalculator.getLightLevel(value);\n        }\n        for (Map.Entry<IValueType<?>, ILightLevelCalculator> entry : lightLevelCalculatorMap.entrySet()) {\n            if(value.canCast(entry.getKey())) {\n                return entry.getValue().getLightLevel(value.cast(entry.getKey()));\n            }\n        }\n        throw new InvalidValueTypeException(Component.translatable(L10NValues.VALUETYPE_ERROR_NOLIGHTCALCULATOR, value));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeLightLevels.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeLightLevelRegistry;\n\n/**\n * Collection of light level calculators for value types..\n * @author rubensworks\n */\npublic class ValueTypeLightLevels {\n\n    public static final IValueTypeLightLevelRegistry REGISTRY = constructRegistry();\n\n    private static IValueTypeLightLevelRegistry constructRegistry() {\n        // This also allows this registry to be used outside of a minecraft environment.\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IValueTypeLightLevelRegistry.class);\n        } else {\n            return ValueTypeLightLevelRegistry.getInstance();\n        }\n    }\n\n    public static void load() {\n        REGISTRY.register(ValueTypes.INTEGER, new IValueTypeLightLevelRegistry.ILightLevelCalculator<ValueTypeInteger.ValueInteger>() {\n            @Override\n            public int getLightLevel(ValueTypeInteger.ValueInteger value) {\n                return Math.max(0, Math.min(value.getRawValue(), 15));\n            }\n        });\n        REGISTRY.register(ValueTypes.BOOLEAN, new IValueTypeLightLevelRegistry.ILightLevelCalculator<ValueTypeBoolean.ValueBoolean>() {\n            @Override\n            public int getLightLevel(ValueTypeBoolean.ValueBoolean value) {\n                return value.getRawValue() ? 15 : 0;\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeList.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeListLPElement;\n\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Value type with values that are strings.\n * @author rubensworks\n */\npublic class ValueTypeList extends ValueObjectTypeBase<ValueTypeList.ValueList> {\n\n    public static final int MAX_RENDER_LINES = 20;\n\n    public ValueTypeList() {\n        super(\"list\", IModHelpers.get().getBaseHelpers().RGBToInt(175, 3, 1), ChatFormatting.DARK_RED, ValueTypeList.ValueList.class);\n    }\n\n    @Override\n    public ValueList getDefault() {\n        return ValueList.ofList(ValueTypes.CATEGORY_ANY, Collections.<IValue>emptyList());\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueList value) {\n        return value.getRawValue().toCompactString();\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueList value) {\n        try {\n            ValueTypeListProxyFactories.REGISTRY.serialize(valueOutput, value.getRawValue());\n        } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public Component canDeserialize(ValueInput valueInput) {\n        try {\n            IValueTypeListProxy<IValueType<IValue>, IValue> proxy = ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput);\n            return null;\n        } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n            return Component.translatable(e.getMessage());\n        }\n    }\n\n    @Override\n    public ValueList deserialize(ValueInput valueInput) {\n        try {\n            IValueTypeListProxy<IValueType<IValue>, IValue> proxy = ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput);\n            return ValueList.ofFactory(proxy);\n        } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n            e.printStackTrace();\n        }\n        return getDefault();\n    }\n\n    @Override\n    public ValueList materialize(ValueList value) throws EvaluationException {\n        IValueTypeListProxy<IValueType<IValue>, IValue> list = value.getRawValue();\n        if (list.isInfinite()) {\n            return ValueList.ofList(list.getValueType(), Lists.newArrayList(list.get(0)));\n        }\n        List<IValue> values = Lists.newArrayListWithExpectedSize(list.getLength());\n        for (IValue listValue : list) {\n            values.add(listValue.getType().materialize(listValue));\n        }\n        return ValueList.ofList(list.getValueType(), values);\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeListLPElement();\n    }\n\n    public static class ValueList<T extends IValueType<V>, V extends IValue> extends ValueBase {\n\n        private final IValueTypeListProxy<T, V> value;\n\n        private ValueList(IValueTypeListProxy<T, V> value) {\n            super(ValueTypes.LIST);\n            this.value = value;\n        }\n\n        public static <T extends IValueType<V>, V extends IValue> ValueList ofList(T valueType, List<V> values) {\n            return new ValueList<>(new ValueTypeListProxyMaterialized<>(valueType, values));\n        }\n\n        public static <V extends IValue> ValueList ofAll(V... values) {\n            return values.length == 0 ? ValueTypes.LIST.getDefault() : ofList(values[0].getType(), ImmutableList.copyOf(values));\n        }\n\n        public static <V extends IValue> ValueList ofAll(IValueType type, V... values) {\n            return ofList(type, ImmutableList.copyOf(values));\n        }\n\n        public static <T extends IValueType<V>, V extends IValue> ValueList ofFactory(IValueTypeListProxy<T, V> proxy) {\n            return new ValueList<>(proxy);\n        }\n\n        public IValueTypeListProxy<T, V> getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueList && ((ValueList) o).value.equals(this.value);\n        }\n\n        @Override\n        public int hashCode() {\n            return value.hashCode();\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueList(value=\" + this.value + \")\";\n        }\n\n    }\n\n    public static class ListFactoryIterator<T extends IValueType<V>, V extends IValue> implements Iterator<V> {\n\n        private final IValueTypeListProxy<T, V> value;\n        private int index = 0;\n        private int length;\n\n        public ListFactoryIterator(IValueTypeListProxy<T, V> value) {\n            this.value = value;\n            try {\n                this.length = this.value.getLength();\n            } catch (EvaluationException e) {\n                this.length = 0;\n            }\n        }\n\n        @Override\n        public boolean hasNext() {\n            return index < length;\n        }\n\n        @Override\n        public V next() {\n            try {\n                return value.get(index++);\n            } catch (EvaluationException e) {\n                e.printStackTrace();\n                return value.getValueType().getDefault();\n            }\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException(\"remove\");\n        }\n    }\n\n    public static class ValueListPredicate extends ValuePredicate<ValueList> {\n\n        private final Optional<Boolean> infinite;\n\n        public ValueListPredicate(Optional<Boolean> infinite) {\n            super(Optional.of(ValueTypes.LIST), Optional.empty(), Optional.empty());\n            this.infinite = infinite;\n        }\n\n        public Optional<Boolean> getInfinite() {\n            return infinite;\n        }\n\n        @Override\n        protected boolean testTyped(ValueList value) {\n            return super.testTyped(value)\n                    && (infinite.isEmpty() || (value.getRawValue().isInfinite() == infinite.get()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyAppend.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\n/**\n * An appended list.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic class ValueTypeListProxyAppend<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final IValueTypeListProxy<T, V> list;\n    private final V value;\n\n    public ValueTypeListProxyAppend(IValueTypeListProxy<T, V> list, V value) {\n        super(ValueTypeListProxyFactories.APPEND.getName(), list.getValueType());\n        this.list = list;\n        this.value = value;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return list.getLength() + 1;\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        int listLength = list.getLength();\n        if (index < listLength) {\n            return list.get(index);\n        } else if (index == listLength) {\n            return value;\n        }\n        return null;\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<IValueType<IValue>, IValue, ValueTypeListProxyAppend<IValueType<IValue>, IValue>> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"append\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxyAppend<IValueType<IValue>, IValue> value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            valueOutput.putString(\"valueType\", value.value.getType().getUniqueName().toString());\n            ValueHelpers.serializeRaw(valueOutput.child(\"v\"), value.value);\n            ValueTypeListProxyFactories.REGISTRY.serialize(valueOutput.child(\"sublist\"), value.list);\n        }\n\n        @Override\n        protected ValueTypeListProxyAppend<IValueType<IValue>, IValue> deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            IValueType valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(valueInput.getString(\"valueType\").orElseThrow()));\n            IValue value = ValueHelpers.deserializeRaw(valueInput.child(\"v\").orElseThrow(), valueType);\n            IValueTypeListProxy<IValueType<IValue>, IValue> list = ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput.child(\"sublist\").orElseThrow());\n            return new ValueTypeListProxyAppend<>(list, value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Iterables;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\n\n/**\n * A list proxy base implementation.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic abstract class ValueTypeListProxyBase<T extends IValueType<V>, V extends IValue> implements IValueTypeListProxy<T, V> {\n\n    private final Identifier name;\n    private final T valueType;\n\n    public ValueTypeListProxyBase(Identifier name, T valueType) {\n        this.name = name;\n        this.valueType = valueType;\n    }\n\n    @Override\n    public T getValueType() {\n        return valueType;\n    }\n\n    @Override\n    public Identifier getName() {\n        return name;\n    }\n\n    @Override\n    public MutableComponent toCompactString() {\n        MutableComponent sb = Component.literal(\"\");\n        boolean first = true;\n        sb.append(\"[\");\n        for(V value : this) {\n            if(!first) {\n                sb.append(\", \");\n            }\n            first = false;\n            sb.append(value.getType().toCompactString(value));\n            if(sb.getString().length() > 10 || true) {\n                sb.append(\"...\");\n                break;\n            }\n        }\n        sb.append(\"]\");\n        return sb;\n    }\n\n    @Override\n    public Iterator<V> iterator() {\n        return new ValueTypeList.ListFactoryIterator<>(this);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if(this == obj)\n            return true;\n\n        if(obj == null || !(obj instanceof ValueTypeListProxyBase)) {\n            return false;\n        }\n        ValueTypeListProxyBase<?, ?> other = (ValueTypeListProxyBase<?, ?>) obj;\n        if(!ValueHelpers.correspondsTo(getValueType(), other.getValueType())) {\n            return false;\n        }\n        // Avoid infinite iteration\n        if (this.isInfinite() || other.isInfinite()) {\n            return false;\n        }\n        // Quickly return if the lengths differ.\n        try {\n            if (getLength() != other.getLength()) {\n                return false;\n            }\n        } catch (EvaluationException e) {\n            return false;\n        }\n\n        Object[] o = Iterables.toArray(this, Object.class);\n        Object[] o2 = Iterables.toArray(other, Object.class);\n        return Arrays.equals(o, o2);\n    }\n\n    @Override\n    public int hashCode() {\n        if(this.isInfinite()) return System.identityHashCode(this);\n        int result = 1;\n        result = 37 * result + getName().hashCode();\n        result = 37 * result + getValueType().hashCode();\n        for(Object obj : this) {\n            result = 37 * result + (obj != null ? obj.hashCode() : 0);\n        }\n        return result;\n    }\n\n    @Override\n    public boolean isInfinite() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyConcat.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\nimport java.util.List;\n\n/**\n * A concatenated list.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic class ValueTypeListProxyConcat<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final IValueTypeListProxy<T, V>[] lists;\n\n    public ValueTypeListProxyConcat(IValueTypeListProxy<T, V>... lists) {\n        super(ValueTypeListProxyFactories.CONCAT.getName(), lists[0].getValueType());\n        this.lists = lists;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        int length = 0;\n        for (IValueTypeListProxy<T, V> list : lists) {\n            length += list.getLength();\n        }\n        return length;\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        for (IValueTypeListProxy<T, V> list : lists) {\n            int currentLength = list.getLength();\n            if (index < currentLength) {\n                return list.get(index);\n            }\n            index -= currentLength;\n        }\n        return null;\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<IValueType<IValue>, IValue, ValueTypeListProxyConcat<IValueType<IValue>, IValue>> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"concat\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxyConcat<IValueType<IValue>, IValue> value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            ValueOutput.ValueOutputList list = valueOutput.childrenList(\"sublists\");\n            for (IValueTypeListProxy<IValueType<IValue>, IValue> listProxy : value.lists) {\n                ValueTypeListProxyFactories.REGISTRY.serialize(list.addChild(), listProxy);\n            }\n        }\n\n        @Override\n        protected ValueTypeListProxyConcat<IValueType<IValue>, IValue> deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            List<IValueTypeListProxy<IValueType<IValue>, IValue>> listProxies = Lists.newArrayList();\n            for (ValueInput child : valueInput.childrenList(\"sublists\").orElseThrow()) {\n                listProxies.add(ValueTypeListProxyFactories.REGISTRY.deserialize(child));\n            }\n            return new ValueTypeListProxyConcat<>(listProxies.toArray(IValueTypeListProxy[]::new));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyEntityArmorInventory.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Iterables;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EquipmentSlotGroup;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\n/**\n * A list proxy for the inventory of an entity.\n */\npublic class ValueTypeListProxyEntityArmorInventory extends ValueTypeListProxyEntityBase<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> implements INBTProvider {\n\n    public ValueTypeListProxyEntityArmorInventory(Level world, Entity entity) {\n        super(ValueTypeListProxyFactories.ENTITY_ARMORINVENTORY.getName(), ValueTypes.OBJECT_ITEMSTACK, world, entity);\n    }\n\n    public ValueTypeListProxyEntityArmorInventory() {\n        this(null, null);\n    }\n\n    protected ItemStack[] getInventory() {\n        Entity e = getEntity();\n        if(e instanceof LivingEntity livingEntity) {\n            return Iterables.toArray(Iterables.transform(EquipmentSlotGroup.ARMOR, livingEntity::getItemBySlot), ItemStack.class);\n        }\n        return new ItemStack[0];\n    }\n\n    @Override\n    public int getLength() {\n        return getInventory().length;\n    }\n\n    @Override\n    public ValueObjectTypeItemStack.ValueItemStack get(int index) {\n        return ValueObjectTypeItemStack.ValueItemStack.of(getInventory()[index]);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyEntityBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\n/**\n * A list proxy for the something of an entity.\n */\npublic abstract class ValueTypeListProxyEntityBase<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> implements INBTProvider {\n\n    private String world;\n    private int entity;\n\n    public ValueTypeListProxyEntityBase(Identifier name, T valueType, Level world, Entity entity) {\n        super(name, valueType);\n        this.world = (world == null ? Level.OVERWORLD : world.dimension()).identifier().toString();\n        this.entity = entity == null ? -1 : entity.getId();\n    }\n\n    protected Entity getEntity() {\n        ServerLevel worldServer = ServerLifecycleHooks.getCurrentServer().getLevel(ResourceKey.create(Registries.DIMENSION, Identifier.parse(this.world)));\n        if(worldServer != null) {\n            return worldServer.getEntity(entity);\n        }\n        return null;\n    }\n\n    @Override\n    public void writeGeneratedFieldsToNBT(ValueOutput output) {\n        output.putString(\"world\", world);\n        output.putInt(\"entity\", entity);\n    }\n\n    @Override\n    public void readGeneratedFieldsFromNBT(ValueInput input) {\n        this.world = input.getString(\"world\").orElseThrow();\n        this.entity = input.getInt(\"entity\").orElseThrow();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyEntityCapability.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.capabilities.EntityCapability;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * A list proxy for a capability of an entity.\n */\npublic abstract class ValueTypeListProxyEntityCapability<C, Context, T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyEntityBase<T, V> {\n\n    private final EntityCapability<C, Context> capability;\n    private Direction side;\n\n    public ValueTypeListProxyEntityCapability(Identifier name, T valueType, Level world, Entity entity,\n                                              EntityCapability<C, Context> capability, @Nullable Direction side) {\n        super(name, valueType, world, entity);\n        this.capability = capability;\n        this.side = side;\n    }\n\n    protected Optional<C> getCapability() {\n        Entity e = getEntity();\n        if(e != null) {\n            return Optional.ofNullable(e.getCapability(this.capability, capability.contextClass() == Direction.class ? (Context) this.side : null));\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public void writeGeneratedFieldsToNBT(ValueOutput output) {\n        super.writeGeneratedFieldsToNBT(output);\n        if (side != null) {\n            output.putInt(\"side\", side.ordinal());\n        }\n    }\n\n    @Override\n    public void readGeneratedFieldsFromNBT(ValueInput input) {\n        super.readGeneratedFieldsFromNBT(input);\n        this.side = input.getInt(\"side\")\n                .map(i -> Direction.values()[i])\n                .orElse(null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyEntityFluids.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\nimport javax.annotation.Nullable;\n\n/**\n * A list proxy for the fluid handler fluids of an entity.\n */\npublic class ValueTypeListProxyEntityFluids extends ValueTypeListProxyEntityCapability<ResourceHandler<FluidResource>, Direction, ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> implements INBTProvider {\n\n    public ValueTypeListProxyEntityFluids(Level world, Entity entity, @Nullable Direction side) {\n        super(ValueTypeListProxyFactories.ENTITY_CAPABILITY_FLUIDS.getName(), ValueTypes.OBJECT_FLUIDSTACK,\n                world, entity, Capabilities.Fluid.ENTITY, side);\n    }\n\n    public ValueTypeListProxyEntityFluids() {\n        this(null, null, null);\n    }\n\n    @Override\n    public int getLength() {\n        return getCapability()\n                .map(ResourceHandler<FluidResource>::size)\n                .orElse(0);\n    }\n\n    @Override\n    public ValueObjectTypeFluidStack.ValueFluidStack get(int index) {\n        return ValueObjectTypeFluidStack.ValueFluidStack.of(getCapability()\n                .map(handler -> handler.getResource(index).toStack(handler.getAmountAsInt(index)))\n                .orElse(FluidStack.EMPTY));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyEntityInventory.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\n/**\n * A list proxy for the inventory of an entity.\n */\npublic class ValueTypeListProxyEntityInventory extends ValueTypeListProxyEntityBase<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> implements INBTProvider {\n\n    public ValueTypeListProxyEntityInventory(Level world, Entity entity) {\n        super(ValueTypeListProxyFactories.ENTITY_INVENTORY.getName(), ValueTypes.OBJECT_ITEMSTACK, world, entity);\n    }\n\n    public ValueTypeListProxyEntityInventory() {\n        this(null, null);\n    }\n\n    protected NonNullList<ItemStack> getInventory() {\n        Entity e = getEntity();\n        if(e != null && e instanceof Player) {\n            return ((Player) e).getInventory().getNonEquipmentItems();\n        }\n        return NonNullList.create();\n    }\n\n    @Override\n    public int getLength() {\n        return getInventory().size();\n    }\n\n    @Override\n    public ValueObjectTypeItemStack.ValueItemStack get(int index) {\n        return ValueObjectTypeItemStack.ValueItemStack.of(getInventory().get(index));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyEntityItems.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\nimport javax.annotation.Nullable;\n\n/**\n * A list proxy for the item handler items of an entity.\n */\npublic class ValueTypeListProxyEntityItems extends ValueTypeListProxyEntityCapability<ResourceHandler<ItemResource>, Void, ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> implements INBTProvider {\n\n    public ValueTypeListProxyEntityItems(Level world, Entity entity, @Nullable Direction side) {\n        super(ValueTypeListProxyFactories.ENTITY_CAPABILITY_ITEMS.getName(), ValueTypes.OBJECT_ITEMSTACK,\n                world, entity, Capabilities.Item.ENTITY, side);\n    }\n\n    public ValueTypeListProxyEntityItems() {\n        this(null, null, null);\n    }\n\n    @Override\n    public int getLength() {\n        return getCapability().map(ResourceHandler::size).orElse(0);\n    }\n\n    @Override\n    public ValueObjectTypeItemStack.ValueItemStack get(int index) {\n        return ValueObjectTypeItemStack.ValueItemStack.of(getCapability().map(handler -> handler.getResource(index).toStack(handler.getAmountAsInt(index))).orElse(ItemStack.EMPTY));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyFactories.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\n/**\n * Collection of light level calculators for value types..\n * @author rubensworks\n */\npublic class ValueTypeListProxyFactories {\n\n    public static final IValueTypeListProxyFactoryTypeRegistry REGISTRY = constructRegistry();\n\n    private static IValueTypeListProxyFactoryTypeRegistry constructRegistry() {\n        // This also allows this registry to be used outside of a minecraft environment.\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IValueTypeListProxyFactoryTypeRegistry.class);\n        } else {\n            return ValueTypeListProxyFactoryTypeRegistry.getInstance();\n        }\n    }\n\n    public static ValueTypeListProxyMaterializedFactory MATERIALIZED;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack, ValueTypeListProxyPositionedInventory> POSITIONED_INVENTORY;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack, ValueTypeListProxyEntityArmorInventory> ENTITY_ARMORINVENTORY;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack, ValueTypeListProxyEntityInventory> ENTITY_INVENTORY;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack, ValueTypeListProxyPositionedTankFluidStacks> POSITIONED_TANK_FLUIDSTACKS;\n    public static ValueTypeListProxyNBTFactory<ValueTypeInteger, ValueTypeInteger.ValueInteger, ValueTypeListProxyPositionedTankCapacities> POSITIONED_TANK_CAPACITIES;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack, ValueTypeListProxyEntityItems> ENTITY_CAPABILITY_ITEMS;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack, ValueTypeListProxyEntityFluids> ENTITY_CAPABILITY_FLUIDS;\n    public static ValueTypeListProxyNBTFactory<ValueObjectTypeRecipe, ValueObjectTypeRecipe.ValueRecipe, ValueTypeListProxyPositionedRecipes> POSITIONED_RECIPES;\n    public static ValueTypeListProxyAppend.Factory APPEND;\n    public static ValueTypeListProxyConcat.Factory CONCAT;\n    public static ValueTypeListProxyLazyBuilt.Factory LAZY_BUILT;\n    public static ValueTypeListProxyTail.Factory TAIL;\n    public static ValueTypeListProxySlice.Factory SLICE;\n    public static ValueTypeListProxyNbtKeys.Factory NBT_KEYS;\n    public static ValueTypeListProxyNbtValueListTag.Factory NBT_VALUE_LIST_TAG;\n    public static ValueTypeListProxyNbtValueListByte.Factory NBT_VALUE_LIST_BYTE;\n    public static ValueTypeListProxyNbtValueListInt.Factory NBT_VALUE_LIST_INT;\n    public static ValueTypeListProxyNbtValueListLong.Factory NBT_VALUE_LIST_LONG;\n    public static ValueTypeListProxyNbtAsListTag.Factory NBT_AS_LIST_TAG;\n    public static ValueTypeListProxyNbtAsListByte.Factory NBT_AS_LIST_BYTE;\n    public static ValueTypeListProxyNbtAsListInt.Factory NBT_AS_LIST_INT;\n    public static ValueTypeListProxyNbtAsListLong.Factory NBT_AS_LIST_LONG;\n    public static ValueTypeListProxyOperatorMapped.Factory MAPPED;\n\n    public static void load() {\n        if(MATERIALIZED == null) {\n            MATERIALIZED = REGISTRY.register(new ValueTypeListProxyMaterializedFactory());\n            POSITIONED_INVENTORY = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_inventory\"), ValueTypeListProxyPositionedInventory.class));\n            ENTITY_ARMORINVENTORY = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"entity_armor_inventory\"), ValueTypeListProxyEntityArmorInventory.class));\n            ENTITY_INVENTORY = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"entity_inventory\"), ValueTypeListProxyEntityInventory.class));\n            POSITIONED_TANK_FLUIDSTACKS = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_tank_fluidstacks\"), ValueTypeListProxyPositionedTankFluidStacks.class));\n            POSITIONED_TANK_CAPACITIES = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_tank_capacities\"), ValueTypeListProxyPositionedTankCapacities.class));\n            ENTITY_CAPABILITY_ITEMS = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"entity_capability_items\"), ValueTypeListProxyEntityItems.class));\n            ENTITY_CAPABILITY_FLUIDS = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"entity_capability_fluids\"), ValueTypeListProxyEntityFluids.class));\n            POSITIONED_RECIPES = REGISTRY.register(new ValueTypeListProxyNBTFactory<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipes\"), ValueTypeListProxyPositionedRecipes.class));\n            APPEND = REGISTRY.register(new ValueTypeListProxyAppend.Factory());\n            CONCAT = REGISTRY.register(new ValueTypeListProxyConcat.Factory());\n            LAZY_BUILT = REGISTRY.register(new ValueTypeListProxyLazyBuilt.Factory());\n            TAIL = REGISTRY.register(new ValueTypeListProxyTail.Factory());\n            SLICE = REGISTRY.register(new ValueTypeListProxySlice.Factory());\n            NBT_KEYS = REGISTRY.register(new ValueTypeListProxyNbtKeys.Factory());\n            NBT_VALUE_LIST_TAG = REGISTRY.register(new ValueTypeListProxyNbtValueListTag.Factory());\n            NBT_VALUE_LIST_BYTE = REGISTRY.register(new ValueTypeListProxyNbtValueListByte.Factory());\n            NBT_VALUE_LIST_INT = REGISTRY.register(new ValueTypeListProxyNbtValueListInt.Factory());\n            NBT_VALUE_LIST_LONG = REGISTRY.register(new ValueTypeListProxyNbtValueListLong.Factory());\n            NBT_AS_LIST_TAG = REGISTRY.register(new ValueTypeListProxyNbtAsListTag.Factory());\n            NBT_AS_LIST_BYTE = REGISTRY.register(new ValueTypeListProxyNbtAsListByte.Factory());\n            NBT_AS_LIST_INT = REGISTRY.register(new ValueTypeListProxyNbtAsListInt.Factory());\n            NBT_AS_LIST_LONG = REGISTRY.register(new ValueTypeListProxyNbtAsListLong.Factory());\n            MAPPED = REGISTRY.register(new ValueTypeListProxyOperatorMapped.Factory());\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyFactoryTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\nimport java.util.Map;\n\n/**\n * Registry for list value type proxies.\n * @author rubensworks\n */\npublic class ValueTypeListProxyFactoryTypeRegistry implements IValueTypeListProxyFactoryTypeRegistry {\n\n    private static ValueTypeListProxyFactoryTypeRegistry INSTANCE = new ValueTypeListProxyFactoryTypeRegistry();\n\n    private final Map<String, IProxyFactory> factories = Maps.newHashMap();\n\n    private ValueTypeListProxyFactoryTypeRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static ValueTypeListProxyFactoryTypeRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>, F extends IProxyFactory<T, V, P>> F register(F proxyFactory) {\n        if(factories.containsKey(proxyFactory.getName())) {\n            throw new RuntimeException(String.format(\"A list proxy factory by name '%s' already exists.\", proxyFactory.getName()));\n        }\n        factories.put(proxyFactory.getName().toString(), proxyFactory);\n        return proxyFactory;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> IProxyFactory<T, V, P> getFactory(Identifier name) {\n        return factories.get(name.toString());\n    }\n\n    @Override\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> void serialize(ValueOutput valueOutput, P proxy) throws SerializationException {\n        IProxyFactory<T, V, P> factory = getFactory(proxy.getName());\n        if (factory == null) {\n            throw new SerializationException(String.format(\"No serialization factory exists for the list proxy type name '%s'.\", proxy.getName()));\n        }\n        valueOutput.putString(\"proxyName\", proxy.getName().toString());\n        factory.serialize(valueOutput.child(\"serialized\"), proxy);\n    }\n\n    @Override\n    public <T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> P deserialize(ValueInput valueInput) throws SerializationException {\n        String name = valueInput.getString(\"proxyName\").orElseThrow();\n        IProxyFactory<T, V, P> factory = getFactory(Identifier.parse(name));\n        if (factory == null) {\n            throw new SerializationException(String.format(\"No deserialization factory exists for the list proxy type name '%s'.\", name));\n        }\n        return factory.deserialize(valueInput.child(\"serialized\").orElseThrow());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyLazyBuilt.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * A list that is built lazily from a start value and an operator.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic class ValueTypeListProxyLazyBuilt<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private Cache<Integer, V> cache_values = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).maximumSize(100).build();\n\n    private final V value;\n    private final IOperator operator;\n\n    public ValueTypeListProxyLazyBuilt(V value, IOperator operator) {\n        super(ValueTypeListProxyFactories.LAZY_BUILT.getName(), (T) value.getType());\n        this.value = value;\n        this.operator = operator;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return Integer.MAX_VALUE;\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        if (index == 0) {\n            return value;\n        }\n        V current = cache_values.getIfPresent(index);\n        if (current != null) {\n            return current;\n        }\n        V previous = get(index - 1);\n        current = (V) operator.evaluate(new IVariable[]{new Variable(previous.getType(), previous)});\n        cache_values.put(index, current);\n        return current;\n    }\n\n    @Override\n    public boolean isInfinite() {\n        return true;\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<IValueType<IValue>, IValue, ValueTypeListProxyLazyBuilt<IValueType<IValue>, IValue>> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"lazybuilt\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxyLazyBuilt<IValueType<IValue>, IValue> value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            valueOutput.putString(\"valueType\", value.value.getType().getUniqueName().toString());\n            ValueHelpers.serializeRaw(valueOutput.child(\"v\"), value.value);\n            Operators.REGISTRY.serialize(valueOutput.child(\"operator\"), value.operator);\n        }\n\n        @Override\n        protected ValueTypeListProxyLazyBuilt<IValueType<IValue>, IValue> deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException, EvaluationException {\n            IValueType valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(valueInput.getString(\"valueType\").orElseThrow()));\n            IValue value = ValueHelpers.deserializeRaw(valueInput.child(\"v\").orElseThrow(), valueType);\n            IOperator operator = Operators.REGISTRY.deserialize(valueInput.child(\"operator\").orElseThrow());\n            return new ValueTypeListProxyLazyBuilt<>(value, operator);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyMaterialized.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport java.util.List;\n\n/**\n * A list proxy for a list that is fully materialized already.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic class ValueTypeListProxyMaterialized<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final List<V> list;\n\n    public ValueTypeListProxyMaterialized(T valueType, List<V> list) {\n        super(ValueTypeListProxyFactories.MATERIALIZED.getName(), valueType);\n        this.list = list;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return list.size();\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        return list.get(index);\n    }\n\n    @Override\n    public String toString() {\n        return list.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyMaterializedFactory.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\n/**\n * Factory for {@link ValueTypeListProxyMaterialized}.\n * @author rubensworks\n */\npublic class ValueTypeListProxyMaterializedFactory implements IValueTypeListProxyFactoryTypeRegistry.IProxyFactory<IValueType<IValue>, IValue, ValueTypeListProxyMaterialized<IValueType<IValue>, IValue>> {\n\n    @Override\n    public Identifier getName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"materialized\");\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueTypeListProxyMaterialized<IValueType<IValue>, IValue> values) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        ValueOutput.ValueOutputList list = valueOutput.childrenList(\"values\");\n\n        // Store headers\n        IValueType<IValue> valueType = values.getValueType();\n        boolean heterogeneous = false;\n        try {\n            // Hack to avoid issue where categories are sometimes used to serialize/deserialize,\n            // which is not allowed (and will crash during deserialization #570).\n            if (valueType.isCategory() && values.getLength() > 0) {\n                heterogeneous = true;\n            }\n        } catch (EvaluationException e) {}\n        valueOutput.putString(\"valueType\", valueType.getUniqueName().toString());\n\n        // Store values\n        for (IValue value : values) {\n            ValueOutput valueTag = list.addChild();\n            if(heterogeneous) {\n                valueTag.putString(\"valueType\", value.getType().getUniqueName().toString());\n            }\n            ValueHelpers.serializeRaw(valueTag, value);\n        }\n    }\n\n    @Override\n    public ValueTypeListProxyMaterialized<IValueType<IValue>, IValue> deserialize(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        String valueTypeName = valueInput.getString(\"valueType\").orElseThrow();\n        IValueType<IValue> valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(valueTypeName));\n        if (valueType == null) {\n            throw new IValueTypeListProxyFactoryTypeRegistry.SerializationException(String.format(\"Could not deserialize the serialized materialized list proxy value because the value type by name '%s' was not found.\", valueTypeName));\n        }\n\n        boolean heterogeneous = valueType.isCategory();\n        IValueType<IValue> elementValueType = valueType;\n\n        ImmutableList.Builder<IValue> builder = ImmutableList.builder();\n        ValueInput.ValueInputList list = valueInput.childrenList(\"values\").orElseThrow();\n        for (ValueInput valueTag : list) {\n            if (heterogeneous) {\n                String subValueTypeName = valueTag.getString(\"valueType\").orElseThrow();\n                elementValueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(subValueTypeName));\n                if (elementValueType == null) {\n                    throw new IValueTypeListProxyFactoryTypeRegistry.SerializationException(String.format(\"Could not deserialize the serialized materialized list proxy value because the value type by name '%s' was not found.\", subValueTypeName));\n                }\n            }\n            IValue deserializedValue = ValueHelpers.deserializeRaw(valueTag, elementValueType);\n            builder.add(deserializedValue);\n        }\n\n        return new ValueTypeListProxyMaterialized<>(valueType, builder.build());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNBTFactory.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\n\n/**\n * Factory for list proxies that implement {@link org.cyclops.cyclopscore.persist.nbt.INBTProvider}.\n * @author rubensworks\n */\npublic class ValueTypeListProxyNBTFactory<T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V> & INBTProvider> implements IValueTypeListProxyFactoryTypeRegistry.IProxyFactory<T, V, P> {\n\n    private final Identifier name;\n    private final Class<P> proxyClass;\n    private final Constructor<P> proxyClassConstructor;\n\n    public ValueTypeListProxyNBTFactory(Identifier name, Class<P> proxyClass) {\n        this.name = name;\n        this.proxyClass = proxyClass;\n\n        try {\n            this.proxyClassConstructor = this.proxyClass.getConstructor();\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n            throw new RuntimeException(String.format(\n                    \"Could not find a default constructor for %s, while this is required for list proxies. This is a developer error.\", proxyClass.getName()));\n        }\n    }\n\n    @Override\n    public Identifier getName() {\n        return this.name;\n    }\n\n    protected Class<P> getProxyClass() {\n        return this.proxyClass;\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, P values) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        values.writeGeneratedFieldsToNBT(valueOutput);\n    }\n\n    @Override\n    public P deserialize(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        try {\n            P proxy = this.proxyClassConstructor.newInstance();\n            proxy.readGeneratedFieldsFromNBT(valueInput);\n            return proxy;\n        } catch (InvocationTargetException | InstantiationException | ClassCastException | IllegalAccessException e) {\n            e.printStackTrace();\n            throw new IValueTypeListProxyFactoryTypeRegistry.SerializationException(e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNBTFactorySimple.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\n/**\n * A base class for list proxy factories that use NBT to store data.\n * @author rubensworks\n */\npublic abstract class ValueTypeListProxyNBTFactorySimple<T extends IValueType<V>, V extends IValue, P extends IValueTypeListProxy<T, V>> implements IValueTypeListProxyFactoryTypeRegistry.IProxyFactory<T, V, P> {\n\n    @Override\n    public void serialize(ValueOutput valueOutput, P value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        serializeNbt(valueOutput, value);\n    }\n\n    @Override\n    public P deserialize(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        try {\n            return deserializeNbt(valueInput);\n        } catch (ClassCastException | EvaluationException e) {\n            e.printStackTrace();\n            throw new IValueTypeListProxyFactoryTypeRegistry.SerializationException(e.getMessage());\n        }\n    }\n\n    protected abstract void serializeNbt(ValueOutput valueOutput, P value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException;\n    protected abstract P deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException, EvaluationException;\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtAsListByte.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.ByteArrayTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT byte array wrapper\n */\npublic class ValueTypeListProxyNbtAsListByte extends ValueTypeListProxyNbtAsListGeneric<ByteArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n    public ValueTypeListProxyNbtAsListByte(Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_AS_LIST_BYTE.getName(), ValueTypes.INTEGER, tag);\n    }\n\n    @Override\n    protected int getLength(ByteArrayTag tag) {\n        return tag.getAsByteArray().length;\n    }\n\n    @Override\n    protected ValueTypeInteger.ValueInteger get(ByteArrayTag tag, int index) {\n        return ValueTypeInteger.ValueInteger.of(tag.getAsByteArray()[index]);\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtAsListGeneric.Factory<ValueTypeListProxyNbtAsListByte, ByteArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_as_value_byte\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtAsListByte create(Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtAsListByte(tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtAsListGeneric.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ExtraCodecs;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\nimport java.util.Optional;\n\n/**\n * An abstraction casting an NBT value to a list of a certain type.\n */\npublic abstract class ValueTypeListProxyNbtAsListGeneric<N extends Tag, T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final Optional<Tag> tag;\n\n    public ValueTypeListProxyNbtAsListGeneric(Identifier name, T valueType, Optional<Tag> tag) {\n        super(name, valueType);\n        this.tag = tag;\n    }\n\n    public Optional<Tag> getTag() {\n        return tag;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        try {\n            return getTag()\n                    .map(t -> getLength((N) t))\n                    .orElse(0);\n        } catch (ClassCastException e) {\n            return 0;\n        }\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        try {\n            if (index < getLength()) {\n                return getTag()\n                        .map(t -> get((N) t, index))\n                        .orElse(null);\n            }\n        } catch (ClassCastException e) {}\n        return null;\n    }\n\n    protected abstract int getLength(N tag);\n    protected abstract V get(N tag, int index);\n\n    public static abstract class Factory<L extends ValueTypeListProxyNbtAsListGeneric<N, T, V>, N extends Tag, T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyNBTFactorySimple<T, V, L> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_as_value\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, L value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            value.getTag().ifPresent(tag -> valueOutput.store(\"tag\", ExtraCodecs.NBT, tag));\n        }\n\n        @Override\n        protected L deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            return create(valueInput.read(\"tag\", ExtraCodecs.NBT));\n        }\n\n        protected abstract L create(Optional<Tag> tag);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtAsListInt.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.IntArrayTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT int array wrapper\n */\npublic class ValueTypeListProxyNbtAsListInt extends ValueTypeListProxyNbtAsListGeneric<IntArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n    public ValueTypeListProxyNbtAsListInt(Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_AS_LIST_INT.getName(), ValueTypes.INTEGER, tag);\n    }\n\n    @Override\n    protected int getLength(IntArrayTag tag) {\n        return tag.getAsIntArray().length;\n    }\n\n    @Override\n    protected ValueTypeInteger.ValueInteger get(IntArrayTag tag, int index) {\n        return ValueTypeInteger.ValueInteger.of(tag.getAsIntArray()[index]);\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtAsListGeneric.Factory<ValueTypeListProxyNbtAsListInt, IntArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_as_value_int\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtAsListInt create(Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtAsListInt(tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtAsListLong.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.LongArrayTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT long array wrapper\n */\npublic class ValueTypeListProxyNbtAsListLong extends ValueTypeListProxyNbtAsListGeneric<LongArrayTag, ValueTypeLong, ValueTypeLong.ValueLong> {\n\n    public ValueTypeListProxyNbtAsListLong(Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_AS_LIST_LONG.getName(), ValueTypes.LONG, tag);\n    }\n\n    @Override\n    protected int getLength(LongArrayTag tag) {\n        return tag.getAsLongArray().length;\n    }\n\n    @Override\n    protected ValueTypeLong.ValueLong get(LongArrayTag tag, int index) {\n        return ValueTypeLong.ValueLong.of(tag.getAsLongArray()[index]);\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtAsListGeneric.Factory<ValueTypeListProxyNbtAsListLong, LongArrayTag, ValueTypeLong, ValueTypeLong.ValueLong> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_as_value_long\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtAsListLong create(Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtAsListLong(tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtAsListTag.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT tag list wrapper\n */\npublic class ValueTypeListProxyNbtAsListTag extends ValueTypeListProxyNbtAsListGeneric<ListTag, ValueTypeNbt, ValueTypeNbt.ValueNbt> {\n\n    public ValueTypeListProxyNbtAsListTag(Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_AS_LIST_TAG.getName(), ValueTypes.NBT, tag);\n    }\n\n    @Override\n    protected int getLength(ListTag tag) {\n        return tag.size();\n    }\n\n    @Override\n    protected ValueTypeNbt.ValueNbt get(ListTag tag, int index) {\n        return ValueTypeNbt.ValueNbt.of(tag.get(index));\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtAsListGeneric.Factory<ValueTypeListProxyNbtAsListTag, ListTag, ValueTypeNbt, ValueTypeNbt.ValueNbt> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_as_value_tag\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtAsListTag create(Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtAsListTag(tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtKeys.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Iterables;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ExtraCodecs;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\nimport java.util.Optional;\n\n/**\n * A list of NBT keys.\n */\npublic class ValueTypeListProxyNbtKeys extends ValueTypeListProxyBase<ValueTypeString, ValueTypeString.ValueString> {\n\n    private final Optional<Tag> tag;\n\n    public ValueTypeListProxyNbtKeys(Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_KEYS.getName(), ValueTypes.STRING);\n        this.tag = tag;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return tag\n                .map(t -> t instanceof CompoundTag ? ((CompoundTag) t).keySet().size() : 0)\n                .orElse(0);\n    }\n\n    @Override\n    public ValueTypeString.ValueString get(int index) throws EvaluationException {\n        if (index < getLength()) {\n            return ValueTypeString.ValueString.of(Iterables.get(((CompoundTag) tag.get()).keySet(), index));\n        }\n        return null;\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<ValueTypeString, ValueTypeString.ValueString, ValueTypeListProxyNbtKeys> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.keys\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxyNbtKeys value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            value.tag.ifPresent(inbt -> valueOutput.store(\"tag\", ExtraCodecs.NBT, inbt));\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtKeys deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            return new ValueTypeListProxyNbtKeys(valueInput.read(\"tag\", ExtraCodecs.NBT));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtValueListByte.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.ByteArrayTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT byte array wrapper\n */\npublic class ValueTypeListProxyNbtValueListByte extends ValueTypeListProxyNbtValueListGeneric<ByteArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n    public ValueTypeListProxyNbtValueListByte(String key, Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_VALUE_LIST_BYTE.getName(), ValueTypes.INTEGER, key, tag);\n    }\n\n    @Override\n    protected int getLength(ByteArrayTag tag) {\n        return tag.getAsByteArray().length;\n    }\n\n    @Override\n    protected ValueTypeInteger.ValueInteger get(ByteArrayTag tag, int index) {\n        return ValueTypeInteger.ValueInteger.of(tag.getAsByteArray()[index]);\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtValueListGeneric.Factory<ValueTypeListProxyNbtValueListByte, ByteArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_value_byte\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtValueListByte create(String key, Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtValueListByte(key, tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtValueListGeneric.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ExtraCodecs;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\nimport java.util.Optional;\n\n/**\n * An abstraction for a list of NBT values of a certain type.\n */\npublic abstract class ValueTypeListProxyNbtValueListGeneric<N extends Tag, T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final String key;\n    private final Optional<CompoundTag> tag;\n\n    public ValueTypeListProxyNbtValueListGeneric(Identifier name, T valueType, String key, Optional<Tag> tag) {\n        super(name, valueType);\n        this.key = key;\n        this.tag = tag.filter(t -> t instanceof CompoundTag).map(t -> (CompoundTag) t);\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public Optional<CompoundTag> getTag() {\n        return tag;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        try {\n            return getTag()\n                    .map(t -> Optional.ofNullable((N) t.get(key)))\n                    .orElse(Optional.empty())\n                    .map(this::getLength)\n                    .orElse(0);\n        } catch (ClassCastException e) {\n            return 0;\n        }\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        try {\n            if (index < getLength()) {\n                return getTag()\n                        .map(t -> Optional.ofNullable((N) t.get(key)))\n                        .orElse(Optional.empty())\n                        .map(t -> get(t, index))\n                        .orElse(null);\n            }\n        } catch (ClassCastException e) {}\n        return null;\n    }\n\n    protected abstract int getLength(N tag);\n    protected abstract V get(N tag, int index);\n\n    public static abstract class Factory<L extends ValueTypeListProxyNbtValueListGeneric<N, T, V>, N extends Tag, T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyNBTFactorySimple<T, V, L> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_value\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, L value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            valueOutput.putString(\"key\", value.getKey());\n            if (value.getTag().isPresent()) {\n                valueOutput.store(\"tag\", ExtraCodecs.NBT, value.getTag().get());\n            }\n        }\n\n        @Override\n        protected L deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            return create(valueInput.getString(\"key\").orElseThrow(), valueInput.read(\"tag\", ExtraCodecs.NBT));\n        }\n\n        protected abstract L create(String key, Optional<Tag> tag);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtValueListInt.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.IntArrayTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT int array wrapper\n */\npublic class ValueTypeListProxyNbtValueListInt extends ValueTypeListProxyNbtValueListGeneric<IntArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n    public ValueTypeListProxyNbtValueListInt(String key, Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_VALUE_LIST_INT.getName(), ValueTypes.INTEGER, key, tag);\n    }\n\n    @Override\n    protected int getLength(IntArrayTag tag) {\n        return tag.getAsIntArray().length;\n    }\n\n    @Override\n    protected ValueTypeInteger.ValueInteger get(IntArrayTag tag, int index) {\n        return ValueTypeInteger.ValueInteger.of(tag.getAsIntArray()[index]);\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtValueListGeneric.Factory<ValueTypeListProxyNbtValueListInt, IntArrayTag, ValueTypeInteger, ValueTypeInteger.ValueInteger> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_value_int\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtValueListInt create(String key, Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtValueListInt(key, tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtValueListLong.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.LongArrayTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * An NBT int array wrapper\n */\npublic class ValueTypeListProxyNbtValueListLong extends ValueTypeListProxyNbtValueListGeneric<LongArrayTag, ValueTypeLong, ValueTypeLong.ValueLong> {\n\n    public ValueTypeListProxyNbtValueListLong(String key, Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_VALUE_LIST_LONG.getName(), ValueTypes.LONG, key, tag);\n    }\n\n    @Override\n    protected int getLength(LongArrayTag tag) {\n        return tag.getAsLongArray().length;\n    }\n\n    @Override\n    protected ValueTypeLong.ValueLong get(LongArrayTag tag, int index) {\n        return ValueTypeLong.ValueLong.of(tag.getAsLongArray()[index]);\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtValueListGeneric.Factory<ValueTypeListProxyNbtValueListLong, LongArrayTag, ValueTypeLong, ValueTypeLong.ValueLong> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_value_long\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtValueListLong create(String key, Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtValueListLong(key, tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyNbtValueListTag.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Optional;\n\n/**\n * A list of NBT tags.\n */\npublic class ValueTypeListProxyNbtValueListTag extends ValueTypeListProxyNbtValueListGeneric<ListTag, ValueTypeNbt, ValueTypeNbt.ValueNbt> {\n\n    public ValueTypeListProxyNbtValueListTag(String key, Optional<Tag> tag) {\n        super(ValueTypeListProxyFactories.NBT_VALUE_LIST_TAG.getName(), ValueTypes.NBT, key, tag);\n    }\n\n    @Override\n    protected int getLength(ListTag tag) {\n        return tag.size();\n    }\n\n    @Override\n    protected ValueTypeNbt.ValueNbt get(ListTag tag, int index) {\n        return ValueTypeNbt.ValueNbt.of(tag.get(index));\n    }\n\n    public static class Factory extends ValueTypeListProxyNbtValueListGeneric.Factory<ValueTypeListProxyNbtValueListTag, ListTag, ValueTypeNbt, ValueTypeNbt.ValueNbt> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"nbt.list_value_tag\");\n        }\n\n        @Override\n        protected ValueTypeListProxyNbtValueListTag create(String key, Optional<Tag> tag) {\n            return new ValueTypeListProxyNbtValueListTag(key, tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyOperatorMapped.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\n/**\n * A list proxy for a list that is mapped to another list by an operator.\n */\npublic class ValueTypeListProxyOperatorMapped extends ValueTypeListProxyBase<IValueType<IValue>, IValue> {\n\n    private final IOperator operator;\n    private final IValueTypeListProxy listProxy;\n\n    public ValueTypeListProxyOperatorMapped(IOperator operator, IValueTypeListProxy listProxy) {\n        super(ValueTypeListProxyFactories.MAPPED.getName(), operator.getInputTypes().length == 1 ? operator.getOutputType() : (IValueType) ValueTypes.OPERATOR);\n        this.operator = operator;\n        this.listProxy = listProxy;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return listProxy.getLength();\n    }\n\n    @Override\n    public IValue get(int index) throws EvaluationException {\n        IValue value = listProxy.get(index);\n        return ValueHelpers.evaluateOperator(operator, value);\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<IValueType<IValue>, IValue, ValueTypeListProxyOperatorMapped> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"mapped\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxyOperatorMapped value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            Operators.REGISTRY.serialize(valueOutput.child(\"operator\"), value.operator);\n            ValueTypeListProxyFactories.REGISTRY.serialize(valueOutput.child(\"sublist\"), value.listProxy);\n        }\n\n        @Override\n        protected ValueTypeListProxyOperatorMapped deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException, EvaluationException {\n            IOperator operator = Operators.REGISTRY.deserialize(valueInput.child(\"operator\").orElseThrow());\n            IValueTypeListProxy<IValueType<IValue>, IValue> list = ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput.child(\"sublist\").orElseThrow());\n            return new ValueTypeListProxyOperatorMapped(operator, list);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyPositioned.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\nimport org.cyclops.cyclopscore.persist.nbt.NBTClassType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\n/**\n * A list proxy for a certain position.\n */\npublic abstract class ValueTypeListProxyPositioned<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> implements INBTProvider {\n\n    private DimPos pos;\n    private Direction side;\n\n    public ValueTypeListProxyPositioned(Identifier name, T valueType, DimPos pos, Direction side) {\n        super(name, valueType);\n        this.pos = pos;\n        this.side = side;\n    }\n\n    @Override\n    public void writeGeneratedFieldsToNBT(ValueOutput output) {\n        NBTClassType.writeNbt(DimPos.class, \"pos\", pos, output);\n        NBTClassType.writeNbt(Direction.class, \"side\", side, output);\n    }\n\n    @Override\n    public void readGeneratedFieldsFromNBT(ValueInput input) {\n        this.pos = NBTClassType.readNbt(DimPos.class, \"pos\", input);\n        this.side = NBTClassType.readNbt(Direction.class, \"side\", input);\n    }\n\n    protected DimPos getPos() {\n        return pos;\n    }\n\n    protected void setPos(DimPos pos) {\n        this.pos = pos;\n    }\n\n    protected Direction getSide() {\n        return side;\n    }\n\n    protected void setSide(Direction side) {\n        this.side = side;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyPositionedInventory.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Iterators;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\nimport javax.annotation.Nullable;\nimport java.util.Iterator;\nimport java.util.Optional;\n\n/**\n * A list proxy for an inventory at a certain position.\n */\npublic class ValueTypeListProxyPositionedInventory extends ValueTypeListProxyPositioned<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack> implements INBTProvider {\n\n    public ValueTypeListProxyPositionedInventory(DimPos pos, Direction side) {\n        super(ValueTypeListProxyFactories.POSITIONED_INVENTORY.getName(), ValueTypes.OBJECT_ITEMSTACK, pos, side);\n    }\n\n    public ValueTypeListProxyPositionedInventory() {\n        this(null, null);\n    }\n\n    protected Optional<ResourceHandler<ItemResource>> getInventory() {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(getPos(), getSide(), net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK);\n    }\n\n    @Override\n    public int getLength() {\n        return getInventory()\n                .map(ResourceHandler<ItemResource>::size)\n                .orElse(0);\n    }\n\n    @Override\n    public ValueObjectTypeItemStack.ValueItemStack get(int index) {\n        return ValueObjectTypeItemStack.ValueItemStack.of(getInventory()\n                .map(itemHandler -> itemHandler.getResource(index).toStack(itemHandler.getAmountAsInt(index)))\n                .orElse(ItemStack.EMPTY));\n    }\n\n    @Override\n    public Iterator<ValueObjectTypeItemStack.ValueItemStack> iterator() {\n        // We use a custom iterator that retrieves the itemhandler capability only once.\n        // Because for large inventories, the capability would have to be retrieved for every single slot,\n        // which could result in a major performance problem.\n        return getInventory()\n                .map(itemHandler -> (Iterator<ValueObjectTypeItemStack.ValueItemStack>) new ValueTypeListProxyPositionedInventory.ListFactoryIterator(itemHandler))\n                .orElse(Iterators.forArray());\n    }\n\n    public static class ListFactoryIterator implements Iterator<ValueObjectTypeItemStack.ValueItemStack> {\n\n        @Nullable\n        private final ResourceHandler<ItemResource> itemHandler;\n        private int index = 0;\n\n        public ListFactoryIterator(@Nullable ResourceHandler<ItemResource> itemHandler) {\n            this.itemHandler = itemHandler;\n        }\n\n        @Override\n        public boolean hasNext() {\n            return itemHandler != null && index < itemHandler.size();\n        }\n\n        @Override\n        public ValueObjectTypeItemStack.ValueItemStack next() {\n            return ValueObjectTypeItemStack.ValueItemStack.of(this.itemHandler == null ? ItemStack.EMPTY : this.itemHandler.getResource(index).toStack(itemHandler.getAmountAsInt(index++)));\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException(\"remove\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyPositionedRecipes.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Iterables;\nimport net.minecraft.core.Direction;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\nimport java.util.Optional;\n\n/**\n * A list proxy for the recipes of a recipe handler at a certain position.\n */\npublic class ValueTypeListProxyPositionedRecipes extends ValueTypeListProxyPositioned<ValueObjectTypeRecipe, ValueObjectTypeRecipe.ValueRecipe> implements INBTProvider {\n\n    public ValueTypeListProxyPositionedRecipes(DimPos pos, Direction side) {\n        super(ValueTypeListProxyFactories.POSITIONED_RECIPES.getName(), ValueTypes.OBJECT_RECIPE, pos, side);\n    }\n\n    public ValueTypeListProxyPositionedRecipes() {\n        this(null, null);\n    }\n\n    protected Optional<IRecipeHandler> getRecipeHandler() {\n        return Optional.ofNullable(getPos().getLevel(true).getCapability(org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK, getPos().getBlockPos(), getSide()));\n    }\n\n    @Override\n    public int getLength() {\n        return getRecipeHandler()\n                .map(recipeHandler -> recipeHandler.getRecipes().size())\n                .orElse(0);\n    }\n\n    @Override\n    public ValueObjectTypeRecipe.ValueRecipe get(int index) {\n        return ValueObjectTypeRecipe.ValueRecipe.of(getRecipeHandler()\n                .map(recipeHandler -> Iterables.get(recipeHandler.getRecipes(), index))\n                .orElse(null));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyPositionedTankCapacities.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.Direction;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\nimport java.util.Optional;\n\n/**\n * A list proxy for a tank's capacities at a certain position.\n */\npublic class ValueTypeListProxyPositionedTankCapacities extends ValueTypeListProxyPositioned<ValueTypeInteger, ValueTypeInteger.ValueInteger> implements INBTProvider {\n\n    public ValueTypeListProxyPositionedTankCapacities(DimPos pos, Direction side) {\n        super(ValueTypeListProxyFactories.POSITIONED_TANK_CAPACITIES.getName(), ValueTypes.INTEGER, pos, side);\n    }\n\n    public ValueTypeListProxyPositionedTankCapacities() {\n        this(null, null);\n    }\n\n    protected Optional<ResourceHandler<FluidResource>> getTank() {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(getPos(), getSide(), net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK);\n    }\n\n    @Override\n    public int getLength() {\n        return getTank()\n                .map(ResourceHandler<FluidResource>::size)\n                .orElse(0);\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger get(int index) {\n        return ValueTypeInteger.ValueInteger.of(getTank()\n                .map(fluidHandler -> fluidHandler.getCapacityAsInt(index, FluidResource.EMPTY))\n                .orElse(0));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyPositionedTankFluidStacks.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.Direction;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.persist.nbt.INBTProvider;\n\nimport java.util.Optional;\n\n/**\n * A list proxy for a tank's fluidstacks at a certain position.\n */\npublic class ValueTypeListProxyPositionedTankFluidStacks extends ValueTypeListProxyPositioned<ValueObjectTypeFluidStack, ValueObjectTypeFluidStack.ValueFluidStack> implements INBTProvider {\n\n    public ValueTypeListProxyPositionedTankFluidStacks(DimPos pos, Direction side) {\n        super(ValueTypeListProxyFactories.POSITIONED_TANK_FLUIDSTACKS.getName(), ValueTypes.OBJECT_FLUIDSTACK, pos, side);\n    }\n\n    public ValueTypeListProxyPositionedTankFluidStacks() {\n        this(null, null);\n    }\n\n    protected Optional<ResourceHandler<FluidResource>> getTank() {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(getPos(), getSide(), net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK);\n    }\n\n    @Override\n    public int getLength() {\n        return getTank()\n                .map(ResourceHandler::size)\n                .orElse(0);\n    }\n\n    @Override\n    public ValueObjectTypeFluidStack.ValueFluidStack get(int index) {\n        return ValueObjectTypeFluidStack.ValueFluidStack.of(getTank()\n                .map(fluidHandler -> fluidHandler.getResource(index).toStack(fluidHandler.getAmountAsInt(index)))\n                .orElse(FluidStack.EMPTY));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxySlice.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\n/**\n * A sliced list.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic class ValueTypeListProxySlice<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final IValueTypeListProxy<T, V> list;\n    private final int from;\n    private final int to;\n\n    public ValueTypeListProxySlice(IValueTypeListProxy<T, V> list, int from, int to) {\n        super(ValueTypeListProxyFactories.SLICE.getName(), list.getValueType());\n        this.list = list;\n        this.from = from;\n        this.to = to;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return Math.max(0, Math.min(list.getLength(), this.to) - this.from);\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        if (index < list.getLength()) {\n            return list.get(this.from + index);\n        }\n        return null;\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<IValueType<IValue>, IValue, ValueTypeListProxySlice<IValueType<IValue>, IValue>> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"slice\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxySlice<IValueType<IValue>, IValue> value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            ValueTypeListProxyFactories.REGISTRY.serialize(valueOutput.child(\"sublist\"), value.list);\n            valueOutput.putInt(\"from\", value.from);\n            valueOutput.putInt(\"to\", value.to);\n        }\n\n        @Override\n        protected ValueTypeListProxySlice<IValueType<IValue>, IValue> deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            IValueTypeListProxy<IValueType<IValue>, IValue> list = ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput.child(\"sublist\").orElseThrow());\n            return new ValueTypeListProxySlice<>(list, valueInput.getInt(\"from\").orElseThrow(), valueInput.getInt(\"to\").orElseThrow());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeListProxyTail.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\n\n/**\n * An list without its first element.\n * @param <T> The value type type.\n * @param <V> The value type.\n */\npublic class ValueTypeListProxyTail<T extends IValueType<V>, V extends IValue> extends ValueTypeListProxyBase<T, V> {\n\n    private final IValueTypeListProxy<T, V> list;\n\n    public ValueTypeListProxyTail(IValueTypeListProxy<T, V> list) {\n        super(ValueTypeListProxyFactories.TAIL.getName(), list.getValueType());\n        this.list = list;\n    }\n\n    @Override\n    public int getLength() throws EvaluationException {\n        return Math.max(0, list.getLength() - 1);\n    }\n\n    @Override\n    public V get(int index) throws EvaluationException {\n        int listLength = list.getLength();\n        if (index < listLength - 1) {\n            return list.get(index + 1);\n        }\n        return null;\n    }\n\n    public static class Factory extends ValueTypeListProxyNBTFactorySimple<IValueType<IValue>, IValue, ValueTypeListProxyTail<IValueType<IValue>, IValue>> {\n\n        @Override\n        public Identifier getName() {\n            return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"tail\");\n        }\n\n        @Override\n        protected void serializeNbt(ValueOutput valueOutput, ValueTypeListProxyTail<IValueType<IValue>, IValue> value) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n            ValueTypeListProxyFactories.REGISTRY.serialize(valueOutput.child(\"sublist\"), value.list);\n        }\n\n        @Override\n        protected ValueTypeListProxyTail<IValueType<IValue>, IValue> deserializeNbt(ValueInput valueInput) throws IValueTypeListProxyFactoryTypeRegistry.SerializationException, EvaluationException {\n            IValueTypeListProxy<IValueType<IValue>, IValue> list = ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput.child(\"sublist\").orElseThrow());\n            return new ValueTypeListProxyTail<>(list);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeLong.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNumber;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.text.NumberFormat;\nimport java.util.Locale;\n\n/**\n * Value type with values that are doubles.\n * @author rubensworks\n */\npublic class ValueTypeLong extends ValueTypeBase<ValueTypeLong.ValueLong> implements IValueTypeNumber<ValueTypeLong.ValueLong> {\n\n    public ValueTypeLong() {\n        super(\"long\", IModHelpers.get().getBaseHelpers().RGBToInt(215, 254, 23), ChatFormatting.YELLOW, ValueTypeLong.ValueLong.class);\n    }\n\n    @Override\n    public ValueLong getDefault() {\n        return ValueLong.of(0L);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueLong value) {\n        return Component.literal(Long.toString(value.getRawValue()));\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueLong value) {\n        valueOutput.putLong(\"v\", value.getRawValue());\n    }\n\n    @Override\n    public ValueLong deserialize(ValueInput valueInput) {\n        return ValueLong.of(valueInput.getLong(\"v\").orElseThrow());\n    }\n\n    @Override\n    public String toString(ValueLong value) {\n        return Long.toString(value.getRawValue());\n    }\n\n    @Override\n    public ValueLong parseString(String value) throws EvaluationException {\n        try {\n            return ValueLong.of(Long.parseLong(value));\n        } catch (NumberFormatException e) {\n            throw new EvaluationException(Component.translatable(L10NValues.OPERATOR_ERROR_PARSE, value,\n                    Component.translatable(getTranslationKey())));\n        }\n    }\n\n    @Override\n    public boolean isZero(ValueLong a) {\n        return a.getRawValue() == 0L;\n    }\n\n    @Override\n    public boolean isOne(ValueLong a) {\n        return a.getRawValue() == 1L;\n    }\n\n    @Override\n    public ValueLong add(ValueLong a, ValueLong b) {\n        return ValueLong.of(a.getRawValue() + b.getRawValue());\n    }\n\n    @Override\n    public ValueLong subtract(ValueLong a, ValueLong b) {\n        return ValueLong.of(a.getRawValue() - b.getRawValue());\n    }\n\n    @Override\n    public ValueLong multiply(ValueLong a, ValueLong b) {\n        return ValueLong.of(a.getRawValue() * b.getRawValue());\n    }\n\n    @Override\n    public ValueLong divide(ValueLong a, ValueLong b) {\n        return ValueLong.of(a.getRawValue() / b.getRawValue());\n    }\n\n    @Override\n    public ValueLong max(ValueLong a, ValueLong b) {\n        return ValueLong.of(Math.max(a.getRawValue(), b.getRawValue()));\n    }\n\n    @Override\n    public ValueLong min(ValueLong a, ValueLong b) {\n        return ValueLong.of(Math.min(a.getRawValue(), b.getRawValue()));\n    }\n\n    @Override\n    public ValueLong increment(ValueLong a) {\n        return ValueLong.of(a.getRawValue() + 1L);\n    }\n\n    @Override\n    public ValueLong decrement(ValueLong a) {\n        return ValueLong.of(a.getRawValue() - 1L);\n    }\n\n    @Override\n    public ValueLong modulus(ValueLong a, ValueLong b) {\n        return ValueLong.of(a.getRawValue() % b.getRawValue());\n    }\n\n    @Override\n    public boolean greaterThan(ValueLong a, ValueLong b) {\n        return a.getRawValue() > b.getRawValue();\n    }\n\n    @Override\n    public boolean lessThan(ValueLong a, ValueLong b) {\n        return a.getRawValue() < b.getRawValue();\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger round(ValueLong a) {\n        return ValueTypeInteger.ValueInteger.of((int) a.getRawValue());\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger ceil(ValueLong a) {\n        return ValueTypeInteger.ValueInteger.of((int) a.getRawValue());\n    }\n\n    @Override\n    public ValueTypeInteger.ValueInteger floor(ValueLong a) {\n        return ValueTypeInteger.ValueInteger.of((int) a.getRawValue());\n    }\n\n    @Override\n    public ValueTypeString.ValueString compact(ValueLong a) {\n        NumberFormat nf = NumberFormat.getCompactNumberInstance(\n            Locale.US,\n            GeneralConfig.numberCompactUseLongStyle ? NumberFormat.Style.LONG : NumberFormat.Style.SHORT\n        );\n        nf.setMinimumFractionDigits(GeneralConfig.numberCompactMinimumFractionDigits);\n        nf.setMaximumFractionDigits(GeneralConfig.numberCompactMaximumFractionDigits);\n        nf.setMinimumIntegerDigits(GeneralConfig.numberCompactMinimumIntegerDigits);\n        nf.setMaximumIntegerDigits(GeneralConfig.numberCompactMaximumIntegerDigits);\n        return ValueTypeString.ValueString.of(nf.format(a.getRawValue()));\n    }\n\n    @Override\n    public String getName(ValueLong a) {\n        return toCompactString(a).getString();\n    }\n\n    public static class ValueLong extends ValueBase {\n\n        private final long value;\n\n        private ValueLong(long value) {\n            super(ValueTypes.LONG);\n            this.value = value;\n        }\n\n        public static ValueLong of(long value) {\n            return new ValueLong(value);\n        }\n\n        public long getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueLong && ((ValueLong) o).value == this.value;\n        }\n\n        @Override\n        public int hashCode() {\n            return getType().hashCode() + (int) value;\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueLong(value=\" + this.value + \")\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeNbt.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.util.ExtraCodecs;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNullable;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Value type with values that are NBT tags.\n * @author rubensworks\n */\npublic class ValueTypeNbt extends ValueTypeBase<ValueTypeNbt.ValueNbt>\n        implements IValueTypeNullable<ValueTypeNbt.ValueNbt>, IValueTypeNamed<ValueTypeNbt.ValueNbt> {\n\n    public ValueTypeNbt() {\n        super(\"nbt\", IModHelpers.get().getBaseHelpers().RGBToInt(0, 170, 170), ChatFormatting.DARK_AQUA, ValueTypeNbt.ValueNbt.class);\n    }\n\n    @Override\n    public ValueNbt getDefault() {\n        return ValueNbt.of();\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueNbt value) {\n        return Component.literal(toString(value));\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueNbt value) {\n        if (value.getRawValue().isPresent()) {\n            valueOutput.store(\"v\", ExtraCodecs.NBT, value.getRawValue().get());\n        }\n    }\n\n    @Override\n    public ValueNbt deserialize(ValueInput valueInput) {\n        return ValueNbt.of(valueInput.read(\"v\", ExtraCodecs.NBT));\n    }\n\n    @Override\n    public String toString(ValueNbt value) {\n        return value.getRawValue().map(Object::toString).orElse(\"\");\n    }\n\n    @Override\n    public ValueNbt parseString(String value) throws EvaluationException {\n        if (value.isEmpty()) {\n            return ValueNbt.of();\n        }\n        try {\n            return ValueNbt.of(Helpers.TAG_PARSER.parseFully(value));\n        } catch (CommandSyntaxException e) {\n            throw new EvaluationException(Component.literal(e.getMessage()));\n        }\n    }\n\n    @Override\n    public boolean isNull(ValueNbt a) {\n        return !a.getRawValue().isPresent();\n    }\n\n    /**\n     * Filter away the blacklisted tags from the given NBT tag.\n     * This won't modify the original tag.\n     * @param tag The tag.\n     * @return The tag where all blacklisted tags have been removed.\n     */\n    public Tag filterBlacklistedTags(Tag tag) {\n        if (tag instanceof CompoundTag) {\n            boolean copied = false;\n            CompoundTag compountTag = (CompoundTag) tag;\n            for (String key : GeneralConfig.nbtTagBlacklist) {\n                if (compountTag.contains(key)) {\n                    if (!copied) {\n                        copied = true;\n                        compountTag = compountTag.copy();\n                    }\n                    compountTag.remove(key);\n                }\n            }\n            return compountTag;\n        }\n        return tag;\n    }\n\n    @Override\n    public String getName(ValueNbt value) {\n        return toCompactString(value).getString();\n    }\n\n    public static class ValueNbt extends ValueOptionalBase<Tag> {\n\n        private ValueNbt(Tag value) {\n            super(ValueTypes.NBT, value);\n        }\n\n        @Nullable\n        @Override\n        protected Tag preprocessValue(@Nullable Tag value) {\n            if (value != null) {\n                return ValueTypes.NBT.filterBlacklistedTags(value);\n            }\n            return null;\n        }\n\n        public static ValueNbt of(@Nullable Tag value) {\n            return new ValueNbt(value);\n        }\n\n        public static ValueNbt of(Optional<Tag> value) {\n            return of(value.orElse(null));\n        }\n\n        public static ValueNbt of() {\n            return of((Tag) null);\n        }\n\n        @Override\n        protected boolean isEqual(Tag a, Tag b) {\n            return a.equals(b);\n        }\n\n        @Override\n        public String toString() {\n            return getRawValue().map(Tag::toString).orElse(\"none\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeUniquelyNamed;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeOperatorLPElement;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\n/**\n * Value type with operator values.\n * @author rubensworks\n */\npublic class ValueTypeOperator extends ValueTypeBase<ValueTypeOperator.ValueOperator> implements\n        IValueTypeNamed<ValueTypeOperator.ValueOperator>, IValueTypeUniquelyNamed<ValueTypeOperator.ValueOperator> {\n\n    private static final String SIGNATURE_LINK = \"->\";\n\n    public ValueTypeOperator() {\n        super(\"operator\", IModHelpers.get().getBaseHelpers().RGBToInt(43, 231, 47), ChatFormatting.DARK_GREEN, ValueTypeOperator.ValueOperator.class);\n    }\n\n    @Override\n    public ValueOperator getDefault() {\n        return ValueOperator.of(Operators.GENERAL_IDENTITY);\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueOperator value) {\n        return value.getRawValue().getLocalizedNameFull();\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueOperator value) {\n        Operators.REGISTRY.serialize(valueOutput.child(\"v\"), value.getRawValue());\n    }\n\n    @Override\n    public ValueOperator deserialize(ValueInput valueInput) {\n        IOperator operator;\n        try {\n            operator = Operators.REGISTRY.deserialize(valueInput.child(\"v\").orElseThrow());\n        } catch (EvaluationException e) {\n            throw new IllegalArgumentException(e.getMessage());\n        }\n        if (operator != null) {\n            return ValueOperator.of(operator);\n        }\n        throw new IllegalArgumentException(String.format(\"Value \\\"%s\\\" could not be parsed to an operator.\", valueInput));\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo, @Nullable ValueOperator value) {\n        super.loadTooltip(tooltipAdder, appendOptionalInfo, value);\n        if (value != null) {\n            tooltipAdder.accept(Component.translatable(L10NValues.VALUETYPEOPERATOR_TOOLTIP_SIGNATURE)\n                    .append(getSignature(value.getRawValue())));\n        }\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return new ValueTypeOperatorLPElement();\n    }\n\n    @Override\n    public ValueOperator materialize(ValueOperator value) throws EvaluationException {\n        return ValueOperator.of(value.getRawValue().materialize());\n    }\n\n    /**\n     * Pretty formatted signature of an operator.\n     * @param operator The operator.\n     * @return The signature.\n     */\n    public static MutableComponent getSignature(IOperator operator) {\n        return getSignatureLines(operator, false)\n                .stream()\n                .reduce(Component.literal(\"\"), (a, b) -> a.append(\" \").append(b));\n    }\n\n    /**\n     * Pretty formatted signature of an operator.\n     * @param inputTypes The input types.\n     * @param outputType The output types.\n     * @return The signature.\n     */\n    public static Component getSignature(IValueType[] inputTypes, IValueType outputType) {\n        return getSignatureLines(inputTypes, outputType, false)\n                .stream()\n                .reduce((prev, next) -> prev.append(\" \").append(next))\n                .orElseGet(() -> Component.literal(\"\"));\n    }\n\n    protected static MutableComponent switchSignatureLineContext(List<MutableComponent> lines, MutableComponent sb) {\n        lines.add(sb);\n        return Component.literal(\"\");\n    }\n\n    /**\n     * Pretty formatted signature of an operator.\n     * @param inputTypes The input types.\n     * @param outputType The output types.\n     * @param indent If the lines should be indented.\n     * @return The signature.\n     */\n    public static List<MutableComponent> getSignatureLines(IValueType[] inputTypes, IValueType outputType, boolean indent) {\n        List<MutableComponent> lines = Lists.newArrayList();\n        MutableComponent sb = Component.literal(\"\");\n        boolean first = true;\n        for (IValueType inputType : inputTypes) {\n            if (first) {\n                first = false;\n            } else {\n                sb = switchSignatureLineContext(lines, sb);\n                sb.append((indent ? \"  \" : \"\") + SIGNATURE_LINK + \" \");\n            }\n            sb = sb.append(Component.translatable(inputType.getTranslationKey())\n                    .withStyle(inputType.getDisplayColorFormat()));\n        }\n\n        sb = switchSignatureLineContext(lines, sb);\n        sb = sb.append((indent ? \"  \" : \"\") + SIGNATURE_LINK + \" \")\n                .append(Component.translatable(outputType.getTranslationKey())\n                        .withStyle(outputType.getDisplayColorFormat()));\n        switchSignatureLineContext(lines, sb);\n        return lines;\n    }\n\n    /**\n     * Pretty formatted signature of an operator.\n     * @param operator The operator.\n     * @param indent If the lines should be indented.\n     * @return The signature.\n     */\n    public static List<MutableComponent> getSignatureLines(IOperator operator, boolean indent) {\n        return getSignatureLines(operator.getInputTypes(), operator.getOutputType(), indent);\n    }\n\n    @Override\n    public String getName(ValueTypeOperator.ValueOperator a) {\n        return a.getRawValue().getLocalizedNameFull().getString();\n    }\n\n    @Override\n    public String getUniqueName(ValueOperator a) {\n        return a.getRawValue().getUniqueName().toString();\n    }\n\n    public static class ValueOperator extends ValueBase {\n\n        private final IOperator value;\n\n        private ValueOperator(IOperator value) {\n            super(ValueTypes.OPERATOR);\n            this.value = value;\n        }\n\n        public static ValueOperator of(IOperator value) {\n            return new ValueOperator(value);\n        }\n\n        public IOperator getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o == this || (o instanceof ValueOperator && value.equals(((ValueOperator) o).value));\n        }\n\n        @Override\n        public int hashCode() {\n            return 37 + value.hashCode();\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueOperator(value=\" + this.value + \")\";\n        }\n    }\n\n    public static class ValueOperatorPredicate extends ValuePredicate<ValueOperator> {\n\n        private final Optional<IOperator> operator;\n\n        public ValueOperatorPredicate(Optional<IOperator> operator) {\n            super(Optional.of(ValueTypes.OPERATOR), Optional.empty(), Optional.empty());\n            this.operator = operator;\n        }\n\n        public Optional<IOperator> getOperator() {\n            return operator;\n        }\n\n        @Override\n        protected boolean testTyped(ValueOperator value) {\n            return super.testTyped(value)\n                    && (operator.isEmpty() || (value.getRawValue() == operator.get()));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Maps;\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariableFacadePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.api.item.IValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.item.TagPathElement;\nimport org.cyclops.integrateddynamics.core.item.ValueTypeVariableFacade;\nimport org.slf4j.Logger;\n\nimport java.util.*;\n\n/**\n * Registry for {@link IValueType}.\n * @author rubensworks\n */\npublic final class ValueTypeRegistry implements IValueTypeRegistry {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    private static ValueTypeRegistry INSTANCE = new ValueTypeRegistry();\n    private static final IValueTypeVariableFacade INVALID_FACADE = new ValueTypeVariableFacade(false, null, (IValue) null);\n\n    private final Map<String, IValueType> valueTypes = Maps.newHashMap();\n    private ValueTypeRegistryClient client;\n\n    private ValueTypeRegistry() {\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            if(IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n                client = new ValueTypeRegistryClient();\n            }\n            IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).registerHandler(this);\n        }\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static ValueTypeRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public IValueTypeRegistryClient getClient() {\n        return this.client;\n    }\n\n    @Override\n    public <V extends IValue, T extends IValueType<V>> T register(T valueType) {\n        valueTypes.put(valueType.getUniqueName().toString(), valueType);\n        return valueType;\n    }\n\n    @Override\n    public <V extends IValue, T extends IValueTypeCategory<V>> T registerCategory(T category) {\n        return register(category);\n    }\n\n    @Override\n    public IValueType getValueType(Identifier name) {\n        return valueTypes.get(name.toString());\n    }\n\n    @Override\n    public Collection<IValueType> getValueTypes() {\n        return Collections.unmodifiableCollection(valueTypes.values());\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"valuetype\");\n    }\n\n    @Override\n    public IValueTypeVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, int id, CompoundTag tag) {\n        if(!tag.contains(\"typeName\") || !tag.contains(\"value\")) {\n            return INVALID_FACADE;\n        }\n        IValueType type = getValueType(Identifier.parse(tag.getString(\"typeName\").orElseThrow()));\n        if(type == null) {\n            return INVALID_FACADE;\n        }\n        IValue value;\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new TagPathElement(tag), LOGGER)) {\n            ValueInput input = TagValueInput.create(\n                    scopedCollector,\n                    valueDeseralizationContext.holderLookupProvider(),\n                    tag.getCompound(\"value\").orElseThrow()\n            );\n            value = ValueHelpers.deserializeRaw(input, type);\n        } catch (IllegalArgumentException | NoSuchElementException e) {\n            return INVALID_FACADE;\n        }\n        return new ValueTypeVariableFacade(id, type, value);\n    }\n\n    @Override\n    public void setVariableFacade(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag, IValueTypeVariableFacade variableFacade) {\n        tag.putString(\"typeName\", variableFacade.getValueType().getUniqueName().toString());\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new TagPathElement(tag), LOGGER)) {\n            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n            ValueHelpers.serializeRaw(valueOutput, variableFacade.getValue());\n            tag.put(\"value\", valueOutput.buildResult());\n        }\n    }\n\n    @Override\n    public boolean isInstance(IVariableFacade variableFacade) {\n        return variableFacade instanceof IValueTypeVariableFacade;\n    }\n\n    @Override\n    public boolean isInstance(IVariable<?> variable) {\n        return variable instanceof IVariable;\n    }\n\n    public static class ValueTypeVariableFacadePredicate extends VariableFacadePredicate<IValueTypeVariableFacade> {\n\n        private final Optional<IValueType> valueType;\n        private final Optional<ValuePredicate> valuePredicate;\n\n        public ValueTypeVariableFacadePredicate(Optional<IValueType> valueType, Optional<ValuePredicate> valuePredicate) {\n            super(IValueTypeVariableFacade.class);\n            this.valueType = valueType;\n            this.valuePredicate = valuePredicate;\n        }\n\n        public Optional<IValueType> getValueType() {\n            return valueType;\n        }\n\n        public Optional<ValuePredicate> getValuePredicate() {\n            return valuePredicate;\n        }\n\n        @Override\n        protected boolean testTyped(IValueTypeVariableFacade variableFacade) {\n            return super.testTyped(variableFacade)\n                    && (valueType.isEmpty() || ValueHelpers.correspondsTo(variableFacade.getValueType(), valueType.get()))\n                    && valuePredicate.orElse(ValuePredicate.ANY).test(variableFacade.getValue());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeRegistryClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.client.color.item.Constant;\nimport net.minecraft.client.renderer.item.CuboidItemModelWrapper;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeRegistryClient;\n\nimport java.util.*;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeRegistryClient implements IValueTypeRegistryClient {\n\n    private Map<IValueType, ItemModel.Unbaked> valueTypeModels = new IdentityHashMap<>();\n\n    @Override\n    public <V extends IValue, T extends IValueType<V>> void registerValueTypeModel(T valueType, Identifier modelLocation) {\n        valueTypeModels.put(valueType, new CuboidItemModelWrapper.Unbaked(modelLocation, java.util.Optional.empty(), List.of(new Constant(-1))));\n    }\n\n    @Override\n    public <V extends IValue, T extends IValueType<V>> ItemModel.Unbaked getValueTypeModel(T valueType) {\n        return valueTypeModels.get(valueType);\n    }\n\n    @Override\n    public Collection<ItemModel.Unbaked> getValueTypeModels() {\n        return Collections.unmodifiableCollection(valueTypeModels.values());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypeString.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeNamed;\n\n/**\n * Value type with values that are strings.\n * @author rubensworks\n */\npublic class ValueTypeString extends ValueTypeBase<ValueTypeString.ValueString>\n        implements IValueTypeNamed<ValueTypeString.ValueString> {\n\n    public ValueTypeString() {\n        super(\"string\", IModHelpers.get().getBaseHelpers().RGBToInt(250, 10, 13), ChatFormatting.RED, ValueTypeString.ValueString.class);\n    }\n\n    @Override\n    public ValueString getDefault() {\n        return ValueString.of(\"\");\n    }\n\n    @Override\n    public MutableComponent toCompactString(ValueString value) {\n        return Component.literal(value.getRawValue());\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, ValueString value) {\n        valueOutput.putString(\"v\", value.getRawValue());\n    }\n\n    @Override\n    public ValueString deserialize(ValueInput valueInput) {\n        return ValueString.of(valueInput.getString(\"v\").orElseThrow());\n    }\n\n    @Override\n    public String toString(ValueString value) {\n        return value.getRawValue();\n    }\n\n    @Override\n    public ValueString parseString(String value) throws EvaluationException {\n        return ValueString.of(value);\n    }\n\n    @Override\n    public String getName(ValueString a) {\n        return a.getRawValue();\n    }\n\n    public static class ValueString extends ValueBase {\n\n        private final String value;\n\n        private ValueString(String value) {\n            super(ValueTypes.STRING);\n            this.value = value;\n        }\n\n        public static ValueString of(String value) {\n            return new ValueString(value);\n        }\n\n        public String getRawValue() {\n            return value;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            return o instanceof ValueString && ((ValueString) o).value.equals(this.value);\n        }\n\n        @Override\n        public int hashCode() {\n            return getType().hashCode() + value.hashCode();\n        }\n\n        @Override\n        public String toString() {\n            return \"ValueString(value=\" + this.value + \")\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueTypes.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeRegistry;\n\n/**\n * Collection of variable types.\n * @author rubensworks\n */\npublic class ValueTypes {\n\n    public static final IValueTypeRegistry REGISTRY = constructRegistry();\n\n    // Raw value types\n    public static ValueTypeBoolean  BOOLEAN  = REGISTRY.register(new ValueTypeBoolean());\n    public static ValueTypeInteger  INTEGER  = REGISTRY.register(new ValueTypeInteger());\n    public static ValueTypeDouble   DOUBLE   = REGISTRY.register(new ValueTypeDouble());\n    public static ValueTypeLong     LONG     = REGISTRY.register(new ValueTypeLong());\n    public static ValueTypeString   STRING   = REGISTRY.register(new ValueTypeString());\n    public static ValueTypeList     LIST     = REGISTRY.register(new ValueTypeList());\n    public static ValueTypeOperator OPERATOR = REGISTRY.register(new ValueTypeOperator());\n    public static ValueTypeNbt      NBT      = REGISTRY.register(new ValueTypeNbt());\n\n    // Object types\n    public static ValueObjectTypeBlock       OBJECT_BLOCK       = REGISTRY.register(new ValueObjectTypeBlock());\n    public static ValueObjectTypeItemStack   OBJECT_ITEMSTACK   = REGISTRY.register(new ValueObjectTypeItemStack());\n    public static ValueObjectTypeEntity      OBJECT_ENTITY      = REGISTRY.register(new ValueObjectTypeEntity());\n    public static ValueObjectTypeFluidStack  OBJECT_FLUIDSTACK  = REGISTRY.register(new ValueObjectTypeFluidStack());\n    public static ValueObjectTypeIngredients OBJECT_INGREDIENTS = REGISTRY.register(new ValueObjectTypeIngredients());\n    public static ValueObjectTypeRecipe      OBJECT_RECIPE      = REGISTRY.register(new ValueObjectTypeRecipe());\n\n    // Categories\n    public static ValueTypeCategoryAny           CATEGORY_ANY            = REGISTRY.registerCategory(new ValueTypeCategoryAny());\n    public static ValueTypeCategoryNumber        CATEGORY_NUMBER         = REGISTRY.registerCategory(new ValueTypeCategoryNumber());\n    public static ValueTypeCategoryNamed         CATEGORY_NAMED          = REGISTRY.registerCategory(new ValueTypeCategoryNamed());\n    public static ValueTypeCategoryUniquelyNamed CATEGORY_UNIQUELY_NAMED = REGISTRY.registerCategory(new ValueTypeCategoryUniquelyNamed());\n    public static ValueTypeCategoryNullable      CATEGORY_NULLABLE       = REGISTRY.registerCategory(new ValueTypeCategoryNullable());\n\n    private static IValueTypeRegistry constructRegistry() {\n        // This also allows this registry to be used outside of a minecraft environment.\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IValueTypeRegistry.class);\n        } else {\n            return ValueTypeRegistry.getInstance();\n        }\n    }\n\n    public static void load() {}\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/Variable.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.VariableAdapter;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\n\nimport java.util.Objects;\n\n/**\n * A default variable implementation.\n * @author rubensworks\n */\npublic class Variable<V extends IValue> extends VariableAdapter<V> {\n\n    private final IValueType<V> type;\n    private final V value;\n\n    public Variable(IValueType<V> type, V value) {\n        this.type = type;\n        this.value = value;\n    }\n\n    public Variable(V value) {\n        this(value.getType(), value);\n    }\n\n    @Override\n    public IValueType<V> getType() {\n        return type;\n    }\n\n    @Override\n    public V getValue() throws EvaluationException {\n        return value;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof Variable)) return false;\n        Variable<?> variable = (Variable<?>) o;\n        return Objects.equals(type, variable.type) &&\n                Objects.equals(value, variable.value);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(type, value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/VariableFacadePredicateTyped.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariableFacadePredicate;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\n\n/**\n * @author rubensworks\n */\npublic class VariableFacadePredicateTyped extends VariableFacadePredicate<IVariableFacade> {\n    private final IVariableFacadeHandler<?> handler;\n\n    public VariableFacadePredicateTyped(IVariableFacadeHandler<?> handler) {\n        super(IVariableFacade.class);\n        this.handler = handler;\n    }\n\n    public IVariableFacadeHandler<?> getHandler() {\n        return handler;\n    }\n\n    @Override\n    protected boolean testTyped(IVariableFacade variableFacade) {\n        return this.handler.isInstance(variableFacade);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/VariablePredicateTyped.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariablePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\n\nimport java.util.Optional;\n\n/**\n * @author rubensworks\n */\npublic class VariablePredicateTyped extends VariablePredicate<IVariable<?>> {\n    private final IVariableFacadeHandler<?> handler;\n\n    public VariablePredicateTyped(IVariableFacadeHandler<?> handler, Optional<IValueType> valueType, Optional<ValuePredicate> valuePredicate) {\n        super((Class) IVariable.class, valueType, valuePredicate);\n        this.handler = handler;\n    }\n\n    public IVariableFacadeHandler<?> getHandler() {\n        return handler;\n    }\n\n    @Override\n    protected boolean testTyped(IVariable<?> variable) {\n        return this.handler.isInstance(variable);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeBoolean.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport com.google.common.base.Predicates;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\n/**\n * GUI element for boolean value types that can be read from and written to checkboxes.\n * @author rubensworks\n */\npublic class GuiElementValueTypeBoolean<G extends Screen, C extends AbstractContainerMenu> implements IGuiInputElementValueType<GuiElementValueTypeBooleanRenderPattern, G, C, GuiElementValueTypeBooleanClient<G, C>> {\n\n    private final ValueTypeBoolean valueType;\n    private Predicate<IValue> validator;\n    private final IConfigRenderPattern renderPattern;\n    private final boolean defaultInputBoolean;\n    private boolean inputBoolean;\n\n    public GuiElementValueTypeBoolean(ValueTypeBoolean valueType, IConfigRenderPattern renderPattern) {\n        this.valueType = valueType;\n        this.validator = Predicates.alwaysTrue();\n        this.renderPattern = renderPattern;\n        defaultInputBoolean = valueType.getDefault().getRawValue();\n    }\n\n    public ValueTypeBoolean getValueType() {\n        return valueType;\n    }\n\n    public Predicate<IValue> getValidator() {\n        return validator;\n    }\n\n    public boolean getDefaultInputBoolean() {\n        return defaultInputBoolean;\n    }\n\n    public boolean isInputBoolean() {\n        return inputBoolean;\n    }\n\n    public boolean getInputBoolean() {\n        return this.inputBoolean;\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        setInputBoolean(((ValueTypeBoolean.ValueBoolean) value).getRawValue());\n    }\n\n    public void setInputBoolean(boolean inputBoolean) {\n        this.inputBoolean = inputBoolean;\n    }\n\n    @Override\n    public void setValidator(Predicate<IValue> validator) {\n        this.validator = validator;\n    }\n\n    @Override\n    public IValue getValue() {\n        return ValueTypeBoolean.ValueBoolean.of(getInputBoolean());\n    }\n\n    @Override\n    public GuiElementValueTypeBooleanClient<G, C> getClient() {\n        return new GuiElementValueTypeBooleanClient<>(this);\n    }\n\n    @Override\n    public Component getName() {\n        return Component.translatable(getValueType().getTranslationKey());\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder) {\n        getValueType().loadTooltip(tooltipAdder, true, null);\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return renderPattern;\n    }\n\n    @Override\n    public void activate() {\n        this.inputBoolean = defaultInputBoolean;\n    }\n\n    @Override\n    public void deactivate() {\n        // Do nothing\n    }\n\n    @Override\n    public Component validate() {\n        if (!this.validator.test(ValueTypeBoolean.ValueBoolean.of(inputBoolean))) {\n            return Component.translatable(L10NValues.VALUE_ERROR);\n        }\n        return null;\n    }\n\n    @Override\n    public int getColor() {\n        return getValueType().getDisplayColor();\n    }\n\n    @Override\n    public String getSymbol() {\n        return IModHelpers.get().getL10NHelpers().localize(getValueType().getTranslationKey());\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        GuiElementValueTypeBoolean<?, ?> that = (GuiElementValueTypeBoolean<?, ?>) o;\n        return defaultInputBoolean == that.defaultInputBoolean\n                && inputBoolean == that.inputBoolean\n                && Objects.equals(valueType, that.valueType)\n                && Objects.equals(validator, that.validator)\n                && Objects.equals(renderPattern, that.renderPattern);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(valueType, validator, renderPattern, defaultInputBoolean, inputBoolean);\n    }\n\n    @Override\n    public String toString() {\n        return \"GuiElementValueTypeBoolean(valueType=\" + valueType + \", validator=\" + validator + \", renderPattern=\" + renderPattern + \", defaultInputBoolean=\" + defaultInputBoolean + \", inputBoolean=\" + inputBoolean + \")\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeBooleanClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueTypeClient;\n\n/**\n * @author rubensworks\n */\npublic class GuiElementValueTypeBooleanClient<G extends Screen, C extends AbstractContainerMenu> implements IGuiInputElementValueTypeClient<GuiElementValueTypeBooleanRenderPattern, G, C> {\n\n    private final GuiElementValueTypeBoolean<G, C> element;\n\n    public GuiElementValueTypeBooleanClient(GuiElementValueTypeBoolean<G, C> element) {\n        this.element = element;\n    }\n\n    @Override\n    public void setValueInGui(GuiElementValueTypeBooleanRenderPattern subGui, boolean sendToServer) {\n        if(subGui != null) {\n            subGui.getCheckbox().setChecked(this.element.getInputBoolean());\n            if (sendToServer) {\n                subGui.sendValueToServer();\n            }\n        }\n    }\n\n    @Override\n    public GuiElementValueTypeBooleanRenderPattern<?, G, C> createSubGui(int baseX, int baseY,\n                                                                         int maxWidth, int maxHeight, G gui, C container) {\n        return new GuiElementValueTypeBooleanRenderPattern<>(this.element, baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeBooleanRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonCheckbox;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeBooleanValueChangedPacket;\n\n/**\n * @author rubensworks\n */\npublic class GuiElementValueTypeBooleanRenderPattern<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu> extends RenderPattern<GuiElementValueTypeBoolean<G, C>, G, C>\n        implements IRenderPatternValueTypeTooltip {\n\n    protected final GuiElementValueTypeBoolean<G, C> element;\n    private boolean renderTooltip = true;\n    private ButtonCheckbox checkbox = null;\n\n    public GuiElementValueTypeBoolean<G, C> getElement() {\n        return element;\n    }\n\n    public ButtonCheckbox getCheckbox() {\n        return checkbox;\n    }\n\n    public GuiElementValueTypeBooleanRenderPattern(GuiElementValueTypeBoolean<G, C> element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                   G gui, C container) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        this.element = element;\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        super.init(guiLeft, guiTop);\n\n        this.checkbox = new ButtonCheckbox(guiLeft + getX(), guiTop + getY(), getElement().getRenderPattern().getWidth(), getElement().getRenderPattern().getHeight(),\n                Component.translatable(this.getElement().getValueType().getTranslationKey()), (entry) -> this.onChecked(this.checkbox.isChecked()));\n\n        boolean value = element.getInputBoolean();\n        this.checkbox.setChecked(value);\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n        this.checkbox.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return this.checkbox.mouseClicked(evt, isDoubleClick) || super.mouseClicked(evt, isDoubleClick);\n    }\n\n    @Override\n    public boolean isRenderTooltip() {\n        return this.renderTooltip;\n    }\n\n    @Override\n    public void setRenderTooltip(boolean renderTooltip) {\n        this.renderTooltip = renderTooltip;\n    }\n\n    protected void onChecked(boolean checked) {\n        if (container instanceof IDirtyMarkListener) {\n            ((IDirtyMarkListener) container).onDirty();\n        }\n        this.getElement().setInputBoolean(checked);\n        sendValueToServer();\n    }\n\n    @Override\n    public void sendValueToServer() {\n        super.sendValueToServer();\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(new LogicProgrammerValueTypeBooleanValueChangedPacket(this.getElement().getInputBoolean()));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeDropdownList.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport com.google.common.base.Predicates;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntry;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntryListener;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Collections;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\n/**\n * GUI element for value type that are displayed using a dropdown list.\n * @author rubensworks\n */\npublic class GuiElementValueTypeDropdownList<T, G extends Screen, C extends AbstractContainerMenu> implements IGuiInputElementValueType<GuiElementValueTypeDropdownListRenderPattern, G, C, GuiElementValueTypeDropdownListClient<T, G, C>>, IDropdownEntryListener<T> {\n\n    private final IValueType valueType;\n    private Predicate<IValue> validator;\n    private final IConfigRenderPattern renderPattern;\n    private String inputString;\n    private Set<IDropdownEntry<T>> dropdownPossibilities = Collections.emptySet();\n    private IDropdownEntryListener<T> dropdownEntryListener = null;\n\n    public GuiElementValueTypeDropdownList(IValueType valueType, IConfigRenderPattern renderPattern) {\n        this.valueType = valueType;\n        this.validator = Predicates.alwaysTrue();\n        this.renderPattern = renderPattern;\n    }\n\n    public IValueType getValueType() {\n        return valueType;\n    }\n\n    public Predicate<IValue> getValidator() {\n        return validator;\n    }\n\n    public String getInputString() {\n        return inputString;\n    }\n\n    public Set<IDropdownEntry<T>> getDropdownPossibilities() {\n        return dropdownPossibilities;\n    }\n\n    public IDropdownEntryListener<T> getDropdownEntryListener() {\n        return dropdownEntryListener;\n    }\n\n    @Override\n    public void setValidator(Predicate<IValue> validator) {\n        this.validator = validator;\n    }\n\n    public void setInputString(String inputString) {\n        this.inputString = inputString;\n    }\n\n    public void setDropdownPossibilities(Set<IDropdownEntry<T>> dropdownPossibilities) {\n        this.dropdownPossibilities = dropdownPossibilities;\n    }\n\n    public void setDropdownEntryListener(IDropdownEntryListener<T> dropdownEntryListener) {\n        this.dropdownEntryListener = dropdownEntryListener;\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        throw new UnsupportedOperationException(\"This method has not been implemented yet\");\n    }\n\n    @Override\n    public IValue getValue() {\n        throw new UnsupportedOperationException(\"This method has not been implemented yet\");\n    }\n\n    @Override\n    public GuiElementValueTypeDropdownListClient<T, G, C> getClient() {\n        return new GuiElementValueTypeDropdownListClient<>(this);\n    }\n\n    @Override\n    public Component getName() {\n        return Component.translatable(getValueType().getTranslationKey());\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder) {\n        getValueType().loadTooltip(tooltipAdder, true, null);\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return renderPattern;\n    }\n\n    @Override\n    public void activate() {\n        this.inputString = \"\";\n    }\n\n    @Override\n    public void deactivate() {\n        this.inputString = null;\n    }\n\n    @Override\n    public Component validate() {\n        try {\n            IValue value = getValueType().parseString(inputString);\n            if (!this.validator.test(value)) {\n                return Component.translatable(L10NValues.VALUE_ERROR);\n            }\n        } catch (EvaluationException e) {\n            return e.getErrorMessage();\n        }\n        return null;\n    }\n\n    @Override\n    public int getColor() {\n        return getValueType().getDisplayColor();\n    }\n\n    @Override\n    public String getSymbol() {\n        return IModHelpers.get().getL10NHelpers().localize(getValueType().getTranslationKey());\n    }\n\n    @Override\n    public void onSetDropdownPossiblity(IDropdownEntry dropdownEntry) {\n        if (dropdownEntryListener != null) {\n            dropdownEntryListener.onSetDropdownPossiblity(dropdownEntry);\n        }\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        GuiElementValueTypeDropdownList<?, ?, ?> that = (GuiElementValueTypeDropdownList<?, ?, ?>) o;\n        return Objects.equals(valueType, that.valueType)\n                && Objects.equals(validator, that.validator)\n                && Objects.equals(renderPattern, that.renderPattern)\n                && Objects.equals(inputString, that.inputString)\n                && Objects.equals(dropdownPossibilities, that.dropdownPossibilities)\n                && Objects.equals(dropdownEntryListener, that.dropdownEntryListener);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(valueType, validator, renderPattern, inputString, dropdownPossibilities, dropdownEntryListener);\n    }\n\n    @Override\n    public String toString() {\n        return \"GuiElementValueTypeDropdownList(valueType=\" + valueType + \", validator=\" + validator + \", renderPattern=\" + renderPattern + \", inputString=\" + inputString + \", dropdownPossibilities=\" + dropdownPossibilities + \", dropdownEntryListener=\" + dropdownEntryListener + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeDropdownListClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueTypeClient;\n\n/**\n * @author rubensworks\n */\npublic class GuiElementValueTypeDropdownListClient<T, G extends Screen, C extends AbstractContainerMenu> implements IGuiInputElementValueTypeClient<GuiElementValueTypeDropdownListRenderPattern, G, C> {\n\n    private final GuiElementValueTypeDropdownList<T, G, C> element;\n\n    public GuiElementValueTypeDropdownListClient(GuiElementValueTypeDropdownList<T, G, C> element) {\n        this.element = element;\n    }\n\n    @Override\n    public void setValueInGui(GuiElementValueTypeDropdownListRenderPattern subGui, boolean sendToServer) {\n        throw new UnsupportedOperationException(\"This method has not been implemented yet\");\n    }\n\n    @Override\n    public GuiElementValueTypeDropdownListRenderPattern<T, ?, G, C> createSubGui(int baseX, int baseY,\n                                                                                 int maxWidth, int maxHeight, G gui, C container) {\n        return new GuiElementValueTypeDropdownListRenderPattern<>(this.element, baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeDropdownListRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntry;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntryListener;\nimport org.cyclops.integrateddynamics.core.client.gui.WidgetTextFieldDropdown;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeStringValueChangedPacket;\n\nimport java.util.Set;\n\n/**\n * A render pattern for value types that can be read from and written to strings.\n * @author rubensworks\n */\npublic class GuiElementValueTypeDropdownListRenderPattern<T, S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu>\n        extends RenderPattern<GuiElementValueTypeDropdownList<T, G, C>, G, C> implements IDropdownEntryListener<T> {\n\n    protected final GuiElementValueTypeDropdownList<T, G, C> element;\n    private WidgetTextFieldDropdown<T> searchField = null;\n\n    public GuiElementValueTypeDropdownList<T, G, C> getElement() {\n        return element;\n    }\n\n    public WidgetTextFieldDropdown<T> getSearchField() {\n        return searchField;\n    }\n\n    public GuiElementValueTypeDropdownListRenderPattern(GuiElementValueTypeDropdownList<T, G, C> element,\n                                                        int baseX, int baseY, int maxWidth, int maxHeight,\n                                                        G gui, C container) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        this.element = element;\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        Font fontRenderer = Minecraft.getInstance().font;\n        int searchWidth = getElement().getRenderPattern().getWidth() - 28;\n        int searchX = getX() + 14;\n        int searchY = getY() + 6;\n        this.searchField = new WidgetTextFieldDropdown<>(fontRenderer, guiLeft + searchX, guiTop + searchY, searchWidth,\n                fontRenderer.lineHeight + 3, Component.translatable(\"gui.cyclopscore.search\"), true, getDropdownPossibilities());\n        this.searchField.setDropdownEntryListener(this);\n        this.searchField.setMaxLength(64);\n        this.searchField.setBordered(false);\n        this.searchField.setVisible(true);\n        this.searchField.setTextColor(ARGB.opaque(16777215));\n        this.searchField.setCanLoseFocus(true);\n        String value = element.getInputString();\n        if (value == null) {\n            value = \"\";\n        }\n        this.searchField.setValue(value);\n        element.setInputString(searchField.getValue());\n        this.searchField.setWidth(searchWidth);\n        this.searchField.setX(guiLeft + (searchX + searchWidth) - this.searchField.getWidth());\n    }\n\n    protected Set<IDropdownEntry<T>> getDropdownPossibilities() {\n        return element.getDropdownPossibilities();\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n        // Textbox\n        searchField.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (searchField.isFocused()) {\n            if (searchField.charTyped(evt)) {\n                onTyped();\n                return true;\n            }\n        }\n        return super.charTyped(evt);\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (searchField.isFocused()) {\n            searchField.keyPressed(evt);\n            onTyped();\n            return true;\n        }\n        return super.keyPressed(evt);\n    }\n\n    public void onTyped() {\n        element.setInputString(searchField.getValue());\n        if (container instanceof IDirtyMarkListener) {\n            ((IDirtyMarkListener) container).onDirty();\n        }\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                new LogicProgrammerValueTypeStringValueChangedPacket(element.getInputString()));\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return searchField.mouseClicked(evt, isDoubleClick) || super.mouseClicked(evt, isDoubleClick);\n    }\n\n    @Override\n    public void onSetDropdownPossiblity(IDropdownEntry dropdownEntry) {\n        element.onSetDropdownPossiblity(dropdownEntry);\n        if (container instanceof IDirtyMarkListener) {\n            ((IDirtyMarkListener) container).onDirty();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeString.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport com.google.common.base.Predicates;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\n/**\n * GUI element for value type that can be read from and written to strings.\n * @author rubensworks\n */\npublic class GuiElementValueTypeString<G extends Screen, C extends AbstractContainerMenu> implements IGuiInputElementValueType<GuiElementValueTypeStringRenderPattern, G, C, GuiElementValueTypeStringClient<G, C>> {\n\n    private final IValueType valueType;\n    private Predicate<IValue> validator;\n    private final IConfigRenderPattern renderPattern;\n    private String defaultInputString;\n    private String inputString;\n\n    public GuiElementValueTypeString(IValueType valueType, IConfigRenderPattern renderPattern) {\n        this.valueType = valueType;\n        this.validator = Predicates.alwaysTrue();\n        this.renderPattern = renderPattern;\n        defaultInputString = ValueHelpers.toString(valueType.getDefault());\n    }\n\n    public IValueType getValueType() {\n        return valueType;\n    }\n\n    public Predicate<IValue> getValidator() {\n        return validator;\n    }\n\n    public String getDefaultInputString() {\n        return defaultInputString;\n    }\n\n    public String getInputString() {\n        return inputString;\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        setInputString(ValueHelpers.toString(value));\n    }\n\n    public void setInputString(String inputString) {\n        this.inputString = inputString;\n    }\n\n    public void setDefaultInputString(String defaultInputString) {\n        this.defaultInputString = defaultInputString;\n    }\n\n    @Override\n    public void setValidator(Predicate<IValue> validator) {\n        this.validator = validator;\n    }\n\n    @Override\n    public IValue getValue() {\n        try {\n            return ValueHelpers.parseString(getValueType(), getInputString());\n        } catch (EvaluationException e) {\n            // Should not occur, as validation must've happened before.\n            return getValueType().getDefault();\n        }\n    }\n\n    @Override\n    public GuiElementValueTypeStringClient<G, C> getClient() {\n        return new GuiElementValueTypeStringClient<>(this);\n    }\n\n    @Override\n    public Component getName() {\n        return Component.translatable(getValueType().getTranslationKey());\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder) {\n        getValueType().loadTooltip(tooltipAdder, true, null);\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return renderPattern;\n    }\n\n    @Override\n    public void activate() {\n        this.inputString = defaultInputString;\n    }\n\n    @Override\n    public void deactivate() {\n        this.inputString = null;\n    }\n\n    @Override\n    public Component validate() {\n        try {\n            IValue value = getValueType().parseString(inputString);\n            if (!this.validator.test(value)) {\n                return Component.translatable(L10NValues.VALUE_ERROR);\n            }\n        } catch (EvaluationException e) {\n            return e.getErrorMessage();\n        }\n        return null;\n    }\n\n    @Override\n    public int getColor() {\n        return getValueType().getDisplayColor();\n    }\n\n    @Override\n    public String getSymbol() {\n        return IModHelpers.get().getL10NHelpers().localize(getValueType().getTranslationKey());\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        GuiElementValueTypeString<?, ?> that = (GuiElementValueTypeString<?, ?>) o;\n        return Objects.equals(valueType, that.valueType)\n                && Objects.equals(validator, that.validator)\n                && Objects.equals(renderPattern, that.renderPattern)\n                && Objects.equals(defaultInputString, that.defaultInputString)\n                && Objects.equals(inputString, that.inputString);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(valueType, validator, renderPattern, defaultInputString, inputString);\n    }\n\n    @Override\n    public String toString() {\n        return \"GuiElementValueTypeString(valueType=\" + valueType + \", validator=\" + validator + \", renderPattern=\" + renderPattern + \", defaultInputString=\" + defaultInputString + \", inputString=\" + inputString + \")\";\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeStringClient.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueTypeClient;\n\n/**\n * @author rubensworks\n */\npublic class GuiElementValueTypeStringClient<G extends Screen, C extends AbstractContainerMenu> implements IGuiInputElementValueTypeClient<GuiElementValueTypeStringRenderPattern, G, C> {\n\n    private final GuiElementValueTypeString<G, C> element;\n\n    public GuiElementValueTypeStringClient(GuiElementValueTypeString<G, C> element) {\n        this.element = element;\n    }\n\n    @Override\n    public void setValueInGui(GuiElementValueTypeStringRenderPattern subGui, boolean sendToServer) {\n        if(subGui != null) {\n            subGui.getTextField().setValue(this.element.getInputString());\n            if (sendToServer) {\n                subGui.sendValueToServer();\n            }\n        }\n    }\n\n    @Override\n    public GuiElementValueTypeStringRenderPattern<?, G, C> createSubGui(int baseX, int baseY,\n                                                                        int maxWidth, int maxHeight, G gui, C container) {\n        return new GuiElementValueTypeStringRenderPattern<>(this.element, baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/GuiElementValueTypeStringRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeStringValueChangedPacket;\n\n/**\n * A render pattern for value types that can be read from and written to strings.\n * @author rubensworks\n */\npublic class GuiElementValueTypeStringRenderPattern<S extends ISubGuiBox, G extends Screen, C extends AbstractContainerMenu> extends RenderPattern<GuiElementValueTypeString<G, C>, G, C> {\n\n    protected final GuiElementValueTypeString<G, C> element;\n    private WidgetTextFieldExtended textField = null;\n\n    public GuiElementValueTypeString<G, C> getElement() {\n        return element;\n    }\n\n    public WidgetTextFieldExtended getTextField() {\n        return textField;\n    }\n\n    public GuiElementValueTypeStringRenderPattern(GuiElementValueTypeString<G, C> element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                  G gui, C container) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        this.element = element;\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        Font fontRenderer = Minecraft.getInstance().font;\n        int searchWidth = getElement().getRenderPattern().getWidth() - 28;\n        int searchX = getX() + 14;\n        int searchY = getY() + 6;\n        this.textField = new WidgetTextFieldExtended(fontRenderer, guiLeft + searchX, guiTop + searchY, searchWidth,\n                fontRenderer.lineHeight + 3, Component.translatable(this.getElement().getValueType().getTranslationKey()), true);\n        this.textField.setMaxLength(512);\n        this.textField.setBordered(false);\n        this.textField.setVisible(true);\n        this.textField.setTextColor(ARGB.opaque(16777215));\n        this.textField.setCanLoseFocus(true);\n        String value = element.getInputString();\n        if (value == null) {\n            value = element.getDefaultInputString();\n        }\n        this.textField.setValue(value);\n        element.setInputString(textField.getValue());\n        this.textField.setWidth(searchWidth);\n        this.textField.setX(guiLeft + (searchX + searchWidth) - this.textField.getWidth());\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n        // Textbox\n        textField.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (textField.isFocused()) {\n            if (textField.charTyped(evt)) {\n                onTyped();\n                return true;\n            }\n        }\n        return super.charTyped(evt);\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (textField.isFocused()) {\n            textField.keyPressed(evt);\n            onTyped();\n            return true;\n        }\n        return super.keyPressed(evt);\n    }\n\n    private void onTyped() {\n        element.setInputString(textField.getValue());\n        if (container instanceof IDirtyMarkListener) {\n            ((IDirtyMarkListener) container).onDirty();\n        }\n        sendValueToServer();\n    }\n\n    @Override\n    public void sendValueToServer() {\n        super.sendValueToServer();\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                new LogicProgrammerValueTypeStringValueChangedPacket(element.getInputString()));\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return textField.mouseClicked(evt, isDoubleClick) || super.mouseClicked(evt, isDoubleClick);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/evaluate/variable/gui/SubGuiValueTypeInfoBase.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable.gui;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.client.gui.container.ContainerScreenExtended;\nimport org.cyclops.cyclopscore.client.gui.image.Images;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.StringHelpers;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElement;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.core.client.gui.subgui.SubGuiBox;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * @author rubensworks\n */\npublic abstract class SubGuiValueTypeInfoBase<S extends ISubGuiBox, G extends ContainerScreenExtended<?>, C extends AbstractContainerMenu> extends SubGuiBox.Base {\n\n    private final IGuiInputElement element;\n    protected final G gui;\n    protected final C container;\n\n    public SubGuiValueTypeInfoBase(G gui, C container, IGuiInputElement<S, G, C, ?> element, int x, int y, int width, int height) {\n        super(Box.DARK, x, y, width, height);\n        this.gui = gui;\n        this.container = container;\n        this.element = element;\n    }\n\n    protected abstract boolean showError();\n\n    protected abstract Component getLastError();\n\n    protected abstract Identifier getTexture();\n\n    protected int getSignalX() {\n        return getWidth() - 22;\n    }\n\n    protected int getSignalY() {\n        return (getHeight() - 12) / 2;\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n\n        int x = guiLeft + getX();\n        int y = guiTop + getY();\n\n        if (this.shouldRenderElementName()) {\n            guiGraphics.text(fontRenderer, element.getName(), x + 2, y + 6, IModHelpers.get().getBaseHelpers().RGBAToInt(240, 240, 240, 255), true);\n        }\n\n        if (showError()) {\n            Component lastError = getLastError();\n            if (lastError != null) {\n                Images.ERROR.draw(guiGraphics, x + getSignalX(), y + getSignalY() - 1);\n            } else {\n                Images.OK.draw(guiGraphics, x + getSignalX(), y + getSignalY() + 1);\n            }\n        }\n    }\n\n    public boolean shouldRenderElementName() {\n        return true;\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n\n        int x = getX();\n        int y = getY();\n\n        if (showError()) {\n            Component lastError = getLastError();\n            if (lastError != null && gui.isHovering(x + getSignalX(), y + getSignalY() - 1, Images.ERROR.getSheetWidth(), Images.ERROR.getSheetHeight(), mouseX, mouseY)) {\n                List<Component> lines = StringHelpers.splitLines(lastError.getString(), IModHelpers.get().getL10NHelpers().getMaxTooltipLineLength(),\n                                ChatFormatting.RED.toString())\n                        .stream()\n                        .map(Component::literal)\n                        .collect(Collectors.toList());\n                gui.drawTooltip(lines, guiGraphics, mouseX, mouseY);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/event/IntegratedDynamicsSetupEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.event;\n\nimport net.neoforged.fml.ModContainer;\nimport net.neoforged.fml.event.lifecycle.ModLifecycleEvent;\n\n/**\n * This event is invoked during the FMLCommonSetupEvent of Integrated Dynamics,\n * and allows registrations towards Integrated Dynamics to be made.\n * @author rubensworks\n */\npublic class IntegratedDynamicsSetupEvent extends ModLifecycleEvent {\n\n    public IntegratedDynamicsSetupEvent(final ModContainer container) {\n        super(container);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/CableHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\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.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.block.IFacadeable;\nimport org.cyclops.integrateddynamics.api.block.cable.ICable;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkCarrier;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkElementProvider;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.capability.facadeable.FacadeableTileMultipartTicking;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.cyclops.integrateddynamics.core.network.event.NetworkInitializedEvent;\nimport org.cyclops.integrateddynamics.item.ItemBlockCable;\n\nimport javax.annotation.Nullable;\nimport java.lang.ref.WeakReference;\nimport java.util.*;\n\n/**\n * Helpers related to cables.\n * @author rubensworks\n */\npublic class CableHelpers {\n\n    public static final Collection<Direction> ALL_SIDES = Sets.newIdentityHashSet();\n    static {\n        for (Direction side : Direction.values()) {\n            ALL_SIDES.add(side);\n        }\n    }\n\n    /**\n     * Get the cable capability at the given position.\n     * If possible, prefer the variant with block state.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional cable capability.\n     */\n    @Deprecated\n    public static Optional<ICable> getCable(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.Cable.BLOCK);\n    }\n\n    /**\n     * Get the cable capability at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @param blockState The block state.\n     * @return The optional cable capability.\n     */\n    public static Optional<ICable> getCable(ILevelExtension world, BlockPos pos, @Nullable Direction side, @Nullable BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.Cable.BLOCK, pos, blockState, null, side));\n    }\n\n    /**\n     * Get the fakeable cable capability at the given position.\n     * If possible, prefer the variant with block state.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional fakeable cable capability.\n     */\n    @Deprecated\n    public static Optional<ICableFakeable> getCableFakeable(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.CableFakeable.BLOCK);\n    }\n\n    /**\n     * Get the fakeable cable capability at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @param blockState The block state.\n     * @return The optional fakeable cable capability.\n     */\n    public static Optional<ICableFakeable> getCableFakeable(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.CableFakeable.BLOCK, pos, blockState, null, side));\n    }\n\n    /**\n     * Get the path element capability at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional path element capability.\n     */\n    public static Optional<IPathElement> getPathElement(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.PathElement.BLOCK, pos, blockState, null, side));\n    }\n\n    /**\n     * Request to update the cable connections of all neighbours of the given position.\n     * @param world The world.\n     * @param pos The center position.\n     * @param sides The sides to update connections for.\n     */\n    public static void updateConnectionsNeighbours(ILevelExtension world, BlockPos pos, Collection<Direction> sides) {\n        for(Direction side : sides) {\n            updateConnections(world, pos.relative(side), side.getOpposite());\n        }\n    }\n\n    /**\n     * Request to update the cable connections at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     */\n    public static void updateConnections(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        getCable(world, pos, side)\n                .ifPresent(ICable::updateConnections);\n    }\n\n    /**\n     * Check if there is a cable at the given position AND if it is connected for the given side.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side to check a connection for.\n     * @param blockState The block state.\n     * @return If there is a cable that is connected.\n     */\n    public static boolean isCableConnected(ILevelExtension world, BlockPos pos, Direction side, BlockState blockState) {\n        return getCable(world, pos, side, blockState)\n                .map(cable -> cable.isConnected(side))\n                .orElse(false);\n    }\n\n    /**\n     * Check if one side of the given cable at the given position can connect to the given side.\n     * To be used when the cable connections are being updated.\n     * This will check if the origin cable can connect to that side,\n     * if there is a cable at the target side and if that cable can connect with this side.\n     * This ignores any current cable connections.\n     * @param world The world.\n     * @param pos The center position.\n     * @param side The side from the center position to check.\n     * @param originCable The cable at the center position.\n     * @return If it can connect.\n     */\n    public static boolean canCableConnectTo(ILevelExtension world, BlockPos pos, Direction side, ICable originCable) {\n        BlockPos neighbourPos = pos.relative(side);\n        return getCable(world, neighbourPos, side.getOpposite())\n                .map(neighbourCable -> originCable.canConnect(neighbourCable, side)\n                        && neighbourCable.canConnect(originCable, side.getOpposite()))\n                .orElse(false);\n    }\n\n    /**\n     * Check if the given position is not a fake cable.\n     * This can mean that there is no cable at all!\n     * But if there is a cable, this method will return true only if it is a real cable.\n     * If possible, prefer the variant with block state.\n     *\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return If there is no fake cable.\n     */\n    @Deprecated\n    public static boolean isNoFakeCable(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return getCableFakeable(world, pos, side)\n                .map(ICableFakeable::isRealCable)\n                .orElse(true);\n    }\n\n    /**\n     * Check if the given position is not a fake cable.\n     * This can mean that there is no cable at all!\n     * But if there is a cable, this method will return true only if it is a real cable.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @param blockState The block state.\n     * @return If there is no fake cable.\n     */\n    public static boolean isNoFakeCable(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return getCableFakeable(world, pos, side, blockState)\n                .map(ICableFakeable::isRealCable)\n                .orElse(true);\n    }\n\n    /**\n     * Disconnect a cable's side.\n     * @param world The cable world.\n     * @param pos The cable position.\n     * @param side The cable side.\n     * @param cable The cable to disconnect.\n     * @param disconnectSide The side to disconnect.\n     */\n    public static void disconnectCable(Level world, BlockPos pos, Direction side, ICable cable, Direction disconnectSide) {\n        // Store the disconnection in the part entity\n        cable.disconnect(disconnectSide);\n\n        // Signal changes\n        cable.updateConnections();\n        Collection<Direction> sidesToUpdate = getCableConnections(cable);\n        sidesToUpdate.add(disconnectSide);\n        CableHelpers.updateConnectionsNeighbours(world, pos, sidesToUpdate);\n\n        // Reinit the networks for this block and the disconnected neighbour.\n        NetworkHelpers.initNetwork(world, pos, side);\n        NetworkHelpers.initNetwork(world, pos.relative(disconnectSide), side.getOpposite());\n    }\n\n    /**\n     * Actions to be performed when a player right clicks on a cable.\n     * @param world The world  of the cable.\n     * @param pos The position of the cable.\n     * @param state The blockstate of the cable.\n     * @param player The player activating the cable.\n     * @param heldItem The item with which the player is right-clicking.\n     * @param side The side of the block the player right-clicked on.\n     * @param cableConnectionHit The side identifying the cable connection that is being activated,\n     *                           this will be null if the center part of the cable is activated.\n     * @return Action result.\n     */\n    public static InteractionResult onCableActivated(Level world, BlockPos pos, BlockState state, Player player,\n                                                    ItemStack heldItem, Direction side, @Nullable Direction cableConnectionHit) {\n        ICable cable = CableHelpers.getCable(world, pos, side).orElse(null);\n        if (cable == null) {\n            return InteractionResult.PASS;\n        }\n\n        if(WrenchHelpers.isWrench(player, heldItem, world, pos, side)) {\n            if (world.isClientSide()) {\n                return InteractionResult.SUCCESS; // Don't do anything client-side\n            }\n            if (player.isSecondaryUseActive()) {\n                removeCable(world, pos, player);\n            } else if (cableConnectionHit != null) {\n                disconnectCable(world, pos, side, cable, cableConnectionHit);\n            } else if (cableConnectionHit == null) {\n                // Reconnect cable side\n                BlockPos neighbourPos = pos.relative(side);\n                ICable neighbourCable = CableHelpers.getCable(world, neighbourPos, side.getOpposite()).orElse(null);\n                if(neighbourCable != null && !cable.isConnected(side) &&\n                        (cable.canConnect(neighbourCable, side) || neighbourCable.canConnect(cable, side.getOpposite()))\n                        ) {\n                    // Notify the reconnection in the part entity of this and the neighbour block,\n                    // since we don't know in which one the disconnection was made.\n                    cable.reconnect(side);\n                    neighbourCable.reconnect(side.getOpposite());\n\n                    // Signal changes\n                    cable.updateConnections();\n                    Collection<Direction> sidesToUpdate = getCableConnections(cable);\n                    sidesToUpdate.add(side);\n                    CableHelpers.updateConnectionsNeighbours(world, pos, sidesToUpdate);\n\n                    // Reinit the networks for this block and the connected neighbour.\n                    NetworkHelpers.initNetwork(world, pos, side);\n                    NetworkHelpers.initNetwork(world, neighbourPos, side.getOpposite());\n                }\n            }\n            return InteractionResult.SUCCESS;\n        }\n        return InteractionResult.PASS;\n    }\n\n    private static WeakReference<LivingEntity> CABLE_PLACER_SNAPSHOT = new WeakReference<>(null);\n\n    /**\n     * This should be called when a cable is added.\n     * This method automatically notifies the neighbours and (re-)initializes the network if this cable carries one.\n     * This should in most cases only be called server-side.\n     * @param world The world.\n     * @param pos The position.\n     */\n    public static void onCableAdded(Level world, BlockPos pos) {\n        LivingEntity placer = CABLE_PLACER_SNAPSHOT.get();\n        if (placer != null) {\n            CableHelpers.onCableAddedByPlayer(world, pos, placer);\n            CABLE_PLACER_SNAPSHOT = new WeakReference<>(null);\n        } else {\n            onCableAddedByPlayerActual(world, pos, null);\n        }\n    }\n\n    /**\n     * This should be called when a cable was added by a player.\n     * This should be called after {@link CableHelpers#onCableAdded(Level, BlockPos)}.\n     * It simply emits an player-sensitive init event on the network bus.\n     * @param world The world.\n     * @param pos The position.\n     * @param placer The entity who placed the cable.\n     */\n    public static void onCableAddedByPlayer(Level world, BlockPos pos, @Nullable LivingEntity placer) {\n        if (world.captureBlockSnapshots) {\n            CABLE_PLACER_SNAPSHOT = new WeakReference<>(placer);\n        } else {\n            onCableAddedByPlayerActual(world, pos, placer);\n        }\n    }\n\n    /**\n     * This should be called when a cable was added by a player.\n     * This should be called after {@link CableHelpers#onCableAdded(Level, BlockPos)}.\n     * It simply emits an player-sensitive init event on the network bus.\n     * @param world The world.\n     * @param pos The position.\n     * @param placer The entity who placed the cable.\n     */\n    public static void onCableAddedByPlayerActual(Level world, BlockPos pos, @Nullable LivingEntity placer) {\n        CableHelpers.updateConnectionsNeighbours(world, pos, CableHelpers.ALL_SIDES);\n        if(!world.isClientSide()) {\n            NetworkHelpers.initNetwork(world, pos, null)\n                    .ifPresent(network -> NeoForge.EVENT_BUS.post(new NetworkInitializedEvent(network, world, pos, placer)));\n        }\n    }\n\n    private static final Map<Pair<ResourceKey<Level>, BlockPos>, Collection<Direction>> CABLE_REMOVING_CONNECTIONS = Maps.newConcurrentMap();\n\n    public static void overrideCableRemovingConnections(Level level, BlockPos pos, Collection<Direction> sides) {\n        CABLE_REMOVING_CONNECTIONS.put(Pair.of(level.dimension(), pos), sides);\n    }\n\n    /**\n     * This should be called when a cable is being removed, while the part entity is still present.\n     * This method won't do anything when called client-side.\n     *\n     * @param world           The world.\n     * @param pos             The position.\n     * @param dropMainElement If the main part element should be dropped.\n     * @param saveState       If the element state should be saved in the item.\n     * @param blockState      The block state.\n     * @param blockEntity     The block entity.\n     * @return If the cable was removed from the network.\n     */\n    public static boolean onCableRemoving(Level world, BlockPos pos, boolean dropMainElement, boolean saveState, BlockState blockState, BlockEntity blockEntity) {\n        CABLE_REMOVING_CONNECTIONS.put(Pair.of(world.dimension(), pos), CableHelpers.getExternallyConnectedCables(world, pos));\n        if (!world.isClientSide() && CableHelpers.isNoFakeCable(world, pos, null)) {\n            INetworkCarrier networkCarrier = NetworkHelpers.getNetworkCarrier(world, pos, null, blockState).orElse(null);\n\n            // Get all drops from the network elements this cable provides.\n            List<ItemStack> itemStacks = Lists.newLinkedList();\n            INetworkElementProvider networkElementProvider = NetworkHelpers.getNetworkElementProvider(world, pos, null, blockState).orElse(null);\n            if (networkElementProvider != null) {\n                for (INetworkElement networkElement : networkElementProvider.createNetworkElements(world, pos)) {\n                    networkElement.addDrops(blockState, blockEntity, itemStacks, dropMainElement, saveState);\n                }\n                for (ItemStack itemStack : itemStacks) {\n                    Block.popResource(world, pos, itemStack);\n                }\n            }\n\n            // If the cable has a network, remove it from the network.\n            if(networkCarrier != null && networkCarrier.getNetwork() != null) {\n                IPathElement pathElement = getPathElement(world, pos, null, blockState)\n                        .orElseThrow(() -> new IllegalStateException(\"Could not find a valid path element capability\"));\n                return onCableRemovingNetwork(blockState, blockEntity, networkCarrier, pathElement);\n            }\n        }\n        return true;\n    }\n\n    public static boolean onCableRemovingNetwork(BlockState blockState, BlockEntity blockEntity, INetworkCarrier networkCarrier, IPathElement pathElement) {\n        INetwork network = networkCarrier.getNetwork();\n        networkCarrier.setNetwork(null);\n        return network.removePathElement(pathElement, null, blockState, blockEntity);\n    }\n\n    /**\n     * This should be called AFTER a cable is removed, at this stage the part entity will not exist anymore.\n     * This method won't do anything when called client-side.\n     * @param world The world.\n     * @param pos The position.\n     * @return If the cable was removed from the network.\n     */\n    public static boolean onCableRemoved(Level world, BlockPos pos) {\n        Collection<Direction> sides = CABLE_REMOVING_CONNECTIONS.remove(Pair.of(world.dimension(), pos));\n        if (sides == null) {\n            sides = Collections.emptyList();\n        }\n        updateConnectionsNeighbours(world, pos, sides);\n        if (!world.isClientSide()) {\n            // Reinit neighbouring networks.\n            for(Direction side : sides) {\n                BlockPos sidePos = pos.relative(side);\n                NetworkHelpers.initNetwork(world, sidePos, side.getOpposite());\n            }\n        }\n        return true;\n    }\n\n    private static boolean removingCable = false;\n    /**\n     * @return If {@link #removeCable} is currently being called.\n     */\n    public static boolean isRemovingCable() {\n        return removingCable;\n    }\n    /**\n     * @param removingCable If the removing cable flag should be set\n     */\n    public static void setRemovingCable(boolean removingCable) {\n        CableHelpers.removingCable = removingCable;\n    }\n\n    /**\n     * Remove a cable.\n     * This will automatically handle sounds, drops,\n     * fakeable cables, network element removal and network (re)intialization.\n     * @param world The world.\n     * @param pos The position.\n     * @param player The player removing the cable or null.\n     */\n    public static void removeCable(Level world, BlockPos pos, @Nullable Player player) {\n        removingCable = true;\n        BlockState blockState = world.getBlockState(pos);\n        ICable cable = getCable(world, pos, null, blockState).orElse(null);\n        ICableFakeable cableFakeable = getCableFakeable(world, pos, null, blockState).orElse(null);\n        IPartContainer partContainer = PartHelpers.getPartContainer(world, pos, null, blockState).orElse(null);\n        BlockEntity blockEntity = world.getBlockEntity(pos);\n        if (cable == null) {\n            removingCable = false;\n            return;\n        }\n\n        CableHelpers.onCableRemoving(world, pos, false, false, blockState, blockEntity);\n        // If the cable has no parts or is not fakeable, remove the block,\n        // otherwise mark the cable as being fake.\n        if (cableFakeable == null || partContainer == null || !partContainer.hasParts()) {\n            cable.destroy();\n        } else {\n            cableFakeable.setRealCable(false);\n        }\n        if (player == null) {\n            IModHelpers.get().getItemStackHelpers().spawnItemStack(world, pos, cable.getItemStack());\n        } else if (!player.isCreative()) {\n            IModHelpers.get().getItemStackHelpers().spawnItemStackToPlayer(world, pos, cable.getItemStack(), player);\n        }\n        CableHelpers.onCableRemoved(world, pos);\n\n        ItemBlockCable.playBreakSound(world, pos, blockState);\n\n        removingCable = false;\n    }\n\n    /**\n     * Check if the target has a facade.\n     * @param world The world.\n     * @param pos The position.\n     * @param blockState The block state.\n     * @return If it has a facade.\n     */\n    public static boolean hasFacade(ILevelExtension world, BlockPos pos, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.Facadeable.BLOCK, pos, blockState, null, null))\n                .map(IFacadeable::hasFacade)\n                .orElse(false);\n    }\n\n    /**\n     * Get the target's facade\n     * @param world The world.\n     * @param pos The position.\n     * @param blockState The block state.\n     * @return The optional facade.\n     */\n    public static Optional<BlockState> getFacade(ILevelExtension world, BlockPos pos, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.Facadeable.BLOCK, pos, blockState, null, null))\n                .flatMap(facadeable -> Optional.ofNullable(facadeable.getFacade()));\n    }\n\n    /**\n     * Get the target's facade from {@link BlockEntityMultipartTicking} without going through capabilities.\n     * This is necessary because some BlockColor's rely on BlockGetter, which do not support capabilities.\n     * @param world The world.\n     * @param pos The position.\n     * @return The optional facade.\n     */\n    public static Optional<BlockState> getFacadeMultipartTicking(BlockGetter world, BlockPos pos) {\n        return IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityMultipartTicking.class)\n                .flatMap(blockEntity -> Optional.ofNullable(new FacadeableTileMultipartTicking(blockEntity).getFacade()));\n    }\n\n    public static boolean isLightTransparent(Level world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return PartHelpers.getPartContainer(world, pos, side, blockState)\n                .map(partContainer -> {\n                    for (Map.Entry<Direction, IPartType<?, ?>> entry : partContainer.getParts().entrySet()) {\n                        IPartType part = entry.getValue();\n                        if (part.forceLightTransparency(partContainer.getPartState(entry.getKey()))) {\n                            return true;\n                        }\n                    }\n                    return false;\n                })\n                .orElse(false);\n    }\n\n    /**\n     * Get the sides the cable is currently connected to.\n     * @param cable A cable.\n     * @return The cable connections.\n     */\n    public static Collection<Direction> getCableConnections(ICable cable) {\n        Collection<Direction> sides = Sets.newIdentityHashSet();\n        for (Direction side : Direction.values()) {\n            if (cable.isConnected(side)) {\n                sides.add(side);\n            }\n        }\n        return sides;\n    }\n\n    /**\n     * Get the sides that are externally connected to the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @return The sides.\n     */\n    public static Collection<Direction> getExternallyConnectedCables(Level world, BlockPos pos) {\n        Collection<Direction> sides = Sets.newIdentityHashSet();\n        for (Direction side : Direction.values()) {\n            if (CableHelpers.isCableConnected(world, pos.relative(side), side.getOpposite(), null)) {\n                sides.add(side);\n            }\n        }\n        return sides;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/Codecs.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonSyntaxException;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport com.mojang.datafixers.util.Pair;\nimport com.mojang.logging.LogUtils;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.DataResult;\nimport com.mojang.serialization.Dynamic;\nimport com.mojang.serialization.DynamicOps;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.IdentifierException;\nimport net.minecraft.advancements.criterion.ItemPredicate;\nimport net.minecraft.core.HolderLookup;\nimport net.minecraft.core.Registry;\nimport net.minecraft.core.WritableRegistry;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtOps;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.ExtraCodecs;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariableFacadePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariablePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.item.TagPathElement;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.OperatorRegistry;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.slf4j.Logger;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * @author rubensworks\n */\npublic class Codecs {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n\n    public static final Codec<CompoundTag> COMPOUND_TAG = Codec.PASSTHROUGH.xmap(\n            dynamic -> (CompoundTag) dynamic.convert(NbtOps.INSTANCE).getValue(),\n            tag -> new Dynamic<>(NbtOps.INSTANCE, tag)\n    );\n\n    public static final Codec<IPartType> PART_TYPE = Codec.STRING.xmap(\n            name -> {\n                IPartType<?, ?> partType = PartTypes.REGISTRY.getPartType(Identifier.parse(name));\n                if (partType == null) {\n                    throw new JsonSyntaxException(\"No part type found with name: \" + name);\n                }\n                return partType;\n            }, (partType) -> partType.getUniqueName().toString());\n\n    public static final Codec<IAspect> ASPECT = Codec.STRING.xmap(\n            name -> {\n                IAspect<?, ?> aspect = Aspects.REGISTRY.getAspect(Identifier.parse(name));\n                if (aspect == null) {\n                    throw new JsonSyntaxException(\"No aspect found with name: \" + name);\n                }\n                return aspect;\n            }, (aspect) -> aspect.getUniqueName().toString());\n\n    public static final Codec<IOperator> OPERATOR = Codec.STRING.xmap(\n            name -> {\n                IOperator operator = Operators.REGISTRY.getOperator(Identifier.parse(name));\n                if (operator == null) {\n                    throw new JsonSyntaxException(\"No operator found with name: \" + name);\n                }\n                return operator;\n            }, (operator) -> operator.getUniqueName().toString());\n\n    public static final Codec<IValueType> VALUE_TYPE = Codec.STRING.xmap(\n            name -> {\n                IValueType<?> valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(name));\n                if (valueType == null) {\n                    throw new JsonSyntaxException(\"Unknown value type '\" + name + \"', valid types are: \"\n                            + ValueTypes.REGISTRY.getValueTypes().stream().map(IValueType::getUniqueName).collect(Collectors.toList()));\n                }\n                return valueType;\n            }, (valueType) -> valueType.getUniqueName().toString());\n\n    public static final Codec<EntityType<? extends Entity>> ENTITY_TYPE = Codec.STRING.xmap(\n            name -> {\n                try {\n                    return BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse(name));\n                } catch (IdentifierException e) {\n                    throw new JsonSyntaxException(\"Invalid entity type name '\" + name + \"'\");\n                }\n            }, (entityType) -> BuiltInRegistries.ENTITY_TYPE.getKey(entityType).toString());\n\n    public static final Codec<ValuePredicate> VALUE = xorCommonList(Arrays.asList(\n            RecordCodecBuilder.<ValueTypeOperator.ValueOperatorPredicate>create(\n                    builder -> builder.group(\n                                    staticTypeField(\"operator\"),\n                                    OPERATOR.optionalFieldOf(\"operator\").forGetter(ValueTypeOperator.ValueOperatorPredicate::getOperator)\n                            )\n                            .apply(builder, (type, operator) -> new ValueTypeOperator.ValueOperatorPredicate(operator))\n            ),\n            RecordCodecBuilder.<ValueObjectTypeItemStack.ValueItemStackPredicate>create(\n                    builder -> builder.group(\n                                    staticTypeField(\"itemstack\"),\n                                    ItemPredicate.CODEC.optionalFieldOf(\"item\").forGetter(ValueObjectTypeItemStack.ValueItemStackPredicate::getItemPredicate)\n                            )\n                            .apply(builder, (type, itemPredicate) -> new ValueObjectTypeItemStack.ValueItemStackPredicate(itemPredicate))\n            ),\n            RecordCodecBuilder.<ValueObjectTypeEntity.ValueEntityPredicate>create(\n                    builder -> builder.group(\n                                    staticTypeField(\"entity\"),\n                                    ENTITY_TYPE.optionalFieldOf(\"entity\").forGetter(ValueObjectTypeEntity.ValueEntityPredicate::getEntityType)\n                            )\n                            .apply(builder, (type, entityType) -> new ValueObjectTypeEntity.ValueEntityPredicate(entityType))\n            ),\n            RecordCodecBuilder.<ValueTypeList.ValueListPredicate>create(\n                    builder -> builder.group(\n                                    staticTypeField(\"list\"),\n                                    Codec.BOOL.optionalFieldOf(\"infinite_list\").forGetter(ValueTypeList.ValueListPredicate::getInfinite)\n                            )\n                            .apply(builder, (type, infinite) -> new ValueTypeList.ValueListPredicate(infinite))\n            ),\n            RecordCodecBuilder.create(\n                    builder -> builder.group(\n                                    staticTypeField(\"serialized\"),\n                                    VALUE_TYPE.fieldOf(\"value_type\").forGetter(v -> (IValueType) v.getValueType().get()),\n                                    ExtraCodecs.JSON.fieldOf(\"value\").forGetter(v -> (JsonElement) v.getValueJson().get())\n                            )\n                            .apply(builder, (raw, valueType, valueJson) -> {\n                                Optional<IValue> value = Optional.empty();\n                                try {\n                                    Tag tag = Helpers.TAG_PARSER.parseFully(valueJson.getAsString());\n                                    if (tag instanceof CompoundTag compoundTag && compoundTag.contains(\"Primitive\")) {\n                                        tag = compoundTag.get(\"Primitive\");\n                                    }\n                                    if (!(tag instanceof CompoundTag)) {\n                                        CompoundTag compoundTag = new CompoundTag();\n                                        compoundTag.put(\"v\", tag);\n                                        tag = compoundTag;\n                                    }\n                                    try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new TagPathElement((CompoundTag) tag), LOGGER)) {\n                                        ValueInput input = TagValueInput.create(\n                                                scopedCollector,\n                                                getHolderLookupProviderDuringWorldLoadHack(),\n                                                (CompoundTag) tag\n                                        );\n                                        value = Optional.of(ValueHelpers.deserializeRaw(input, valueType));\n                                    }\n                                } catch (CommandSyntaxException e) {\n                                    e.printStackTrace();\n                                }\n                                return new ValuePredicate(Optional.of(valueType), value, Optional.of(valueJson));\n                            })\n            )\n    ));\n\n    public static HolderLookup.Provider getHolderLookupProviderDuringWorldLoadHack() {\n        return new HolderLookup.Provider() {\n            @Override\n            public Stream<ResourceKey<? extends Registry<?>>> listRegistryKeys() {\n                return (Stream) BuiltInRegistries.WRITABLE_REGISTRY.registryKeySet().stream();\n            }\n\n            @Override\n            public <T> Optional<HolderLookup.RegistryLookup<T>> lookup(ResourceKey<? extends Registry<? extends T>> key) {\n                WritableRegistry<T> registry = (WritableRegistry) BuiltInRegistries.WRITABLE_REGISTRY.getValue((ResourceKey) key);\n                return Optional.of(registry);\n            }\n        };\n    }\n\n    public static Codec<VariablePredicate> getVariableCodec() { // This is a function so we can achieve recursion using lazyInitializedCodec\n        return xorCommonList(Arrays.asList(\n                RecordCodecBuilder.<OperatorRegistry.OperatorVariablePredicate>create(\n                        builder -> builder.group(\n                                        staticTypeField(\"operator\"),\n                                        VALUE_TYPE.optionalFieldOf(\"value_type\").forGetter(OperatorRegistry.OperatorVariablePredicate::getValueType),\n                                        VALUE.optionalFieldOf(\"value\").forGetter(OperatorRegistry.OperatorVariablePredicate::getValuePredicate),\n                                        OPERATOR.optionalFieldOf(\"operator\").forGetter(OperatorRegistry.OperatorVariablePredicate::getOperator),\n                                        ExtraCodecs.strictUnboundedMap(Codec.STRING.xmap(\n                                                Integer::parseInt,\n                                                integer -> Integer.toString(integer)\n                                        ), Codec.lazyInitialized(Codecs::getVariableCodec)).optionalFieldOf(\"input\").forGetter(OperatorRegistry.OperatorVariablePredicate::getInputPredicates)\n                                )\n                                .apply(builder, (type, valueType, value, operator, input) -> new OperatorRegistry.OperatorVariablePredicate(valueType, value, operator, input))\n                ),\n                RecordCodecBuilder.<AspectRegistry.AspectVariablePredicate>create(\n                        builder -> builder.group(\n                                        staticTypeField(\"aspect\"),\n                                        VALUE_TYPE.optionalFieldOf(\"value_type\").forGetter(AspectRegistry.AspectVariablePredicate::getValueType),\n                                        VALUE.optionalFieldOf(\"value\").forGetter(AspectRegistry.AspectVariablePredicate::getValuePredicate),\n                                        ASPECT.optionalFieldOf(\"aspect\").forGetter(AspectRegistry.AspectVariablePredicate::getAspect)\n                                )\n                                .apply(builder, (type, valueType, value, aspect) -> new AspectRegistry.AspectVariablePredicate(valueType, value, aspect))\n                ),\n                RecordCodecBuilder.create(\n                        builder -> builder.group(\n                                        staticTypeField(\"value_type\"),\n                                        VALUE_TYPE.optionalFieldOf(\"value_type\").forGetter(VariablePredicate::getValueType),\n                                        VALUE.optionalFieldOf(\"value\").forGetter(VariablePredicate::getValuePredicate)\n                                )\n                                .apply(builder, (type, valueType, valuePredicate) -> new VariablePredicate<>(IVariable.class, valueType, valuePredicate))\n                ),\n                RecordCodecBuilder.<VariablePredicateTyped>create(\n                        builder -> builder.group(\n                                        Codec.STRING.validate(type -> IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).getHandler(Identifier.parse(type)) == null ?\n                                                DataResult.error(() -> \"Variable facade predicate is expected to have as 'type' one of: \" + String.join(\", \", IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).getHandlerNames())) :\n                                                DataResult.success(type)).fieldOf(\"type\").forGetter(p -> p.getHandler().getUniqueName().toString()),\n                                        VALUE_TYPE.optionalFieldOf(\"value_type\").forGetter(VariablePredicate::getValueType),\n                                        VALUE.optionalFieldOf(\"value\").forGetter(VariablePredicate::getValuePredicate)\n                                )\n                                .apply(builder, (type, valueType, valuePredicate) -> new VariablePredicateTyped(IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).getHandler(Identifier.parse(type)), valueType, valuePredicate))\n                )\n        ));\n    }\n    public static final Codec<VariablePredicate> VARIABLE = getVariableCodec();\n\n    public static final Codec<VariableFacadePredicate> VARIABLE_FACADE = xorCommonList(Arrays.asList(\n            RecordCodecBuilder.<AspectRegistry.AspectVariableFacadePredicate>create(\n                    builder -> builder.group(\n                                    staticTypeField(\"aspect\"),\n                                    ASPECT.optionalFieldOf(\"aspect\").forGetter(AspectRegistry.AspectVariableFacadePredicate::getAspect)\n                            )\n                            .apply(builder, (type, aspect) -> new AspectRegistry.AspectVariableFacadePredicate(aspect))\n            ),\n            RecordCodecBuilder.<ValueTypeRegistry.ValueTypeVariableFacadePredicate>create(\n                    builder -> builder.group(\n                                    staticTypeField(\"value_type\"),\n                                    VALUE_TYPE.optionalFieldOf(\"value_type\").forGetter(ValueTypeRegistry.ValueTypeVariableFacadePredicate::getValueType),\n                                    VALUE.optionalFieldOf(\"value\").forGetter(ValueTypeRegistry.ValueTypeVariableFacadePredicate::getValuePredicate)\n                            )\n                            .apply(builder, (type, valueType, value) -> new ValueTypeRegistry.ValueTypeVariableFacadePredicate(valueType, value))\n            ),\n            RecordCodecBuilder.<VariableFacadePredicateTyped>create(\n                    builder -> builder.group(\n                                    Codec.STRING.validate(type -> IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).getHandler(Identifier.parse(type)) == null ?\n                                            DataResult.error(() -> \"Variable facade predicate is expected to have as 'type' one of: \" + String.join(\", \", IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).getHandlerNames())) :\n                                            DataResult.success(type)).fieldOf(\"type\").forGetter(p -> p.getHandler().getUniqueName().toString())\n                            )\n                            .apply(builder, (type) -> new VariableFacadePredicateTyped(IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).getHandler(Identifier.parse(type))))\n            )\n    ));\n\n    public static final Codec<org.apache.commons.lang3.tuple.Pair<? extends SlotDisplay, Float>> SLOT_DISPLAY_CHANCE = RecordCodecBuilder.create(\n            builder -> builder.group(\n                    SlotDisplay.CODEC.fieldOf(\"display\").forGetter(org.apache.commons.lang3.tuple.Pair::getLeft),\n                    Codec.FLOAT.fieldOf(\"chance\").forGetter(org.apache.commons.lang3.tuple.Pair::getRight)\n            ).apply(builder, org.apache.commons.lang3.tuple.Pair::of)\n    );\n\n    public static final StreamCodec<RegistryFriendlyByteBuf, org.apache.commons.lang3.tuple.Pair<? extends SlotDisplay, Float>> STREAM_SLOT_DISPLAY_CHANCE = StreamCodec.of(\n            (buffer, pair) -> {\n                SlotDisplay.STREAM_CODEC.encode(buffer, pair.getLeft());\n                ByteBufCodecs.FLOAT.encode(buffer, pair.getRight());\n            },\n            buffer -> org.apache.commons.lang3.tuple.Pair.of(SlotDisplay.STREAM_CODEC.decode(buffer), ByteBufCodecs.FLOAT.decode(buffer))\n    );\n\n    public static <T> RecordCodecBuilder<T, String> staticTypeField(String value) {\n        return Codec.STRING.validate(type -> !value.equals(type) ?\n                DataResult.error(() -> \"Variable facade predicate is expected to have 'type': '\" + value + \"'\") :\n                DataResult.success(type)).fieldOf(\"type\").forGetter(p -> value);\n    }\n\n    public static <X> Codec<X> xorCommonList(List<Codec<? extends X>> codecs) {\n        Codec<X> codec = (Codec<X>) codecs.get(0);\n        for (Codec<? extends X> codecEntry : codecs.subList(1, codecs.size())) {\n            codec = xorCommon(codec, codecEntry);\n        }\n        return codec;\n    }\n\n    public static <X, F extends X, S extends X> Codec<X> xorCommon(Codec<F> p_144640_, Codec<S> p_144641_) {\n        return new XorCodecCommon<>(p_144640_, p_144641_);\n    }\n\n    static record XorCodecCommon<X, F extends X, S extends X>(Codec<F> first, Codec<S> second) implements Codec<X> {\n        @Override\n        public <T> DataResult<Pair<X, T>> decode(DynamicOps<T> p_144679_, T p_144680_) {\n            DataResult<Pair<X, T>> dataresult = this.first.decode(p_144679_, p_144680_).map(p_144673_ -> (Pair<X, T>) p_144673_);\n            DataResult<Pair<X, T>> dataresult1 = this.second.decode(p_144679_, p_144680_).map(p_144673_ -> (Pair<X, T>) p_144673_);\n            Optional<Pair<X, T>> optional = dataresult.result();\n            Optional<Pair<X, T>> optional1 = dataresult1.result();\n            if (optional.isPresent() && optional1.isPresent()) {\n                return DataResult.error(\n                        () -> \"Both alternatives read successfully, can not pick the correct one; first: \" + optional.get() + \" second: \" + optional1.get(),\n                        optional.get()\n                );\n            } else if (optional.isPresent()) {\n                return dataresult;\n            } else {\n                return optional1.isPresent() ? dataresult1 : dataresult.apply2((p_300790_, p_300791_) -> p_300791_, dataresult1);\n            }\n        }\n\n        public <T> DataResult<T> encode(X p_144663_, DynamicOps<T> p_144664_, T p_144665_) {\n            try {\n                return this.first.encode((F) p_144663_, p_144664_, p_144665_);\n            } catch (ClassCastException e) {\n                return this.second.encode((S) p_144663_, p_144664_, p_144665_);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"XorCodecCommon[\" + this.first + \", \" + this.second + \"]\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/EnergyHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Helpers related to energy.\n * @author rubensworks\n */\npublic class EnergyHelpers {\n\n    private static final List<EnergyHandlerProxy> ENERGY_STORAGE_PROXIES = Lists.newArrayList();\n\n    public static void addEnergyStorageProxy(EnergyHandlerProxy energyStorageProxy) {\n        ENERGY_STORAGE_PROXIES.add(energyStorageProxy);\n    }\n\n    public static Optional<EnergyHandler> getEnergyStorage(PartPos pos) {\n        return getEnergyStorage(pos.getPos(), pos.getSide());\n    }\n\n    public static Optional<EnergyHandler> getEnergyStorage(DimPos pos, Direction facing) {\n        Level world = pos.getLevel(true);\n        return world != null ? getEnergyStorage(world, pos.getBlockPos(), facing) : Optional.empty();\n    }\n\n    public static Optional<EnergyHandler> getEnergyStorage(Level world, BlockPos pos, Direction facing) {\n        EnergyHandler energyStorage = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, facing, net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK)\n                .orElseGet(() -> {\n                    for (EnergyHandlerProxy energyStorageProxy : ENERGY_STORAGE_PROXIES) {\n                        Optional<EnergyHandler> optionalEnergyStorage = energyStorageProxy.getEnergyStorageProxy(world, pos, facing);\n                        if (optionalEnergyStorage.isPresent()) {\n                            return optionalEnergyStorage.orElse(null);\n                        }\n                    }\n                    return null;\n                });\n        return energyStorage == null ? Optional.empty() : Optional.of(energyStorage);\n    }\n\n    /**\n     * Attempty to fill the neighbouring tiles with energy.\n     * @param world The world.\n     * @param pos The filler's position.\n     * @param energy The energy to add.\n     * @param simulate If the filling should be simulated.\n     * @return The amount of energy that was filled somewhere.\n     */\n    public static int fillNeigbours(Level world, BlockPos pos, int energy, boolean simulate) {\n        int toFill = energy;\n        for(Direction side : Direction.values()) {\n            EnergyHandler energyStorage = getEnergyStorage(world, pos.relative(side), side.getOpposite()).orElse(null);\n            if(energyStorage != null) {\n                try (var tx = Transaction.openRoot()) {\n                    toFill -= energyStorage.insert(toFill, tx);\n                    if (!simulate) {\n                        tx.commit();\n                    }\n                }\n                if(toFill <= 0) {\n                    return energy;\n                }\n            }\n        }\n        return energy - toFill;\n    }\n\n    public static interface EnergyHandlerProxy {\n        public Optional<EnergyHandler> getEnergyStorageProxy(BlockGetter world, BlockPos pos, Direction facing);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/Helpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport com.google.common.base.Predicate;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Lists;\nimport net.minecraft.IdentifierException;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.HolderSet;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.NbtOps;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.TagParser;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.tags.TagKey;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.BlockItem;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\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.LiquidBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.Fluid;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.fluid.FluidUtil;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\n\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\n/**\n * Helper methods.\n * @author rubensworks\n */\npublic final class Helpers {\n\n    public static final Predicate<Entity> SELECTOR_IS_PLAYER = entity -> entity instanceof Player;\n\n    public static final TagParser<Tag> TAG_PARSER = TagParser.create(NbtOps.INSTANCE);\n\n    /**\n     * Get the fluidstack from the given itemstack.\n     * @param itemStack The itemstack.\n     * @return The fluidstack or null.\n     */\n    public static FluidStack getFluidStack(ItemStack itemStack) {\n        FluidStack fluidStack = FluidUtil.getFirstStackContained(itemStack);\n        if (fluidStack.isEmpty()\n                && itemStack.getItem() instanceof BlockItem\n                && ((BlockItem) itemStack.getItem()).getBlock() instanceof LiquidBlock) {\n            fluidStack = new FluidStack(((LiquidBlock) ((BlockItem) itemStack.getItem()).getBlock()).fluid, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());\n        }\n        return fluidStack;\n    }\n\n    /**\n     * Get the fluidstack capacity from the given itemstack.\n     * @param itemStack The itemstack.\n     * @return The capacity\n     */\n    public static long getFluidStackCapacity(ItemStack itemStack) {\n        ResourceHandler<FluidResource> fluidHandler = ItemAccess.forStack(itemStack).getCapability(Capabilities.Fluid.ITEM);\n        if (fluidHandler != null) {\n            if (fluidHandler.size() > 0) {\n                return fluidHandler.getCapacityAsLong(0, FluidResource.EMPTY);\n            }\n        }\n        return 0;\n    }\n\n    /**\n     * Retrieves a Stream of items that are registered to this tag name.\n     *\n     * @param name The tag name\n     * @return A Stream containing ItemStacks registered for this tag\n     */\n    public static Stream<ItemStack> getTagValues(String name) throws IdentifierException {\n        Optional<HolderSet.Named<Item>> tag = BuiltInRegistries.ITEM\n                .get(TagKey.create(Registries.ITEM, Identifier.parse(name)));\n        return tag.stream().flatMap(named -> named.stream().map(ItemStack::new));\n    }\n\n    /**\n     * Retrieves a Stream of blocks that are registered to this tag name.\n     *\n     * @param name The tag name\n     * @return A Stream containing Blocks registered for this tag\n     */\n    public static Stream<BlockState> getBlockTagValues(String name) throws IdentifierException {\n        Optional<HolderSet.Named<Block>> tag = BuiltInRegistries.BLOCK\n                .get(TagKey.create(Registries.BLOCK, Identifier.parse(name)));\n        return tag.stream().flatMap(named -> named.stream().map(block -> block.value().defaultBlockState()));\n    }\n\n    /**\n     * Retrieves a Stream of fluids that are registered to this tag name.\n     *\n     * @param name The tag name\n     * @return A Stream containing Fluids registered for this tag\n     */\n    public static Stream<FluidStack> getFluidTagValues(String name) throws IdentifierException {\n        Optional<HolderSet.Named<Fluid>> tag = BuiltInRegistries.FLUID\n                .get(TagKey.create(Registries.FLUID, Identifier.parse(name)));\n        return tag.stream().flatMap(named -> named.stream().map(fluid -> new FluidStack(fluid, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume())));\n    }\n\n    /**\n     * Add the given element to a copy of the given list/\n     * @param list The list.\n     * @param newElement The element.\n     * @param <T> The type.\n     * @return The new joined list.\n     */\n    public static <T> List<T> joinList(List<T> list, T newElement) {\n        ImmutableList.Builder<T> builder = ImmutableList.<T>builder().addAll(list);\n        if(newElement != null) {\n            builder.add(newElement);\n        }\n        return builder.build();\n    }\n\n    /**\n     * Create a string of 'length' times '%s' seperated by ','.\n     * @param length The length for the series of '%s'.\n     * @return The string.\n     */\n    public static String createPatternOfLength(int length) {\n        StringBuilder pattern = new StringBuilder();\n        boolean first = true;\n        for (int i = 0; i < length; i++) {\n            if (first) {\n                first = false;\n            } else {\n                pattern.append(\",\");\n            }\n            pattern.append(\"%s\");\n        }\n        return pattern.toString();\n    }\n\n    private static final List<IInterfaceRetriever> INTERFACE_RETRIEVERS = Lists.newArrayList();\n    static {\n        addInterfaceRetriever(new IInterfaceRetriever() {\n            @Override\n            public <C> Optional<C> getInterface(BlockGetter world, BlockPos pos, Class<C> clazz) {\n                return IModHelpers.get().getBlockEntityHelpers().get(world, pos, clazz);\n            }\n        });\n    }\n\n    /**\n     * Check for the given interface at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param clazz The class to find.\n     * @param <C> The class type.\n     * @return The optional instance.\n     */\n    private static <C> Optional<C> getInterface(BlockGetter world, BlockPos pos, Class<C> clazz) {\n        for(IInterfaceRetriever interfaceRetriever : INTERFACE_RETRIEVERS) {\n            Optional<C> optionalInstance = interfaceRetriever.getInterface(world, pos, clazz);\n            if(optionalInstance.isPresent()) {\n                return optionalInstance;\n            }\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Check for the given interface at the given position.\n     * @param dimPos The dimensional position.\n     * @param clazz The class to find.\n     * @param forceLoad If the world should be loaded if it was not loaded yet.\n     * @param <C> The class type.\n     * @return The optional instance.\n     */\n    public static <C> Optional<C> getInterface(DimPos dimPos, Class<C> clazz, boolean forceLoad) {\n        Level world = dimPos.getLevel(forceLoad);\n        return world != null ? getInterface(world, dimPos.getBlockPos(), clazz) : Optional.empty();\n    }\n\n    /**\n     * Get a localized string showing the ratio of stored energy vs the capacity.\n     * @param stored The stored amount of energy.\n     * @param capacity The capacity of the energy container.\n     * @return The localized string.\n     */\n    public static Component getLocalizedEnergyLevel(int stored, int capacity) {\n        return Component.literal(String.format(Locale.ROOT, \"%,d\", stored))\n                .append(\" / \")\n                .append(String.format(Locale.ROOT, \"%,d\", capacity))\n                .append(\" \")\n                .append(Component.translatable(L10NValues.GENERAL_ENERGY_UNIT));\n    }\n\n    // This is copied from Forge's TPSCommand\n    public static double calculateTps(long[] times) {\n        double worldTickTime = mean(times) * 1.0E-6D;\n        double worldTPS = Math.min(1000.0 / worldTickTime, 20);\n        return worldTPS;\n    }\n\n    public static long mean(long[] values) {\n        long sum = 0L;\n        for (long v : values)\n            sum += v;\n        return sum / values.length;\n    }\n\n    public static void addInterfaceRetriever(IInterfaceRetriever interfaceRetriever) {\n        INTERFACE_RETRIEVERS.add(interfaceRetriever);\n    }\n\n    /**\n     * Return a string with the first character capitalized.\n     * @param value A string.\n     * @return A capitalized string.\n     */\n    public static String capitalizeString(String value) {\n        if (value == null) {\n            return null;\n        } else if (value.length() == 0) {\n            return \"\";\n        } else {\n            return Character.toTitleCase(value.charAt(0)) + value.substring(1);\n        }\n    }\n\n    public static void returnItemToPlayer(Player player, ItemStack itemStack) {\n        if (player.isAlive() && (!(player instanceof ServerPlayer) || !((ServerPlayer)player).hasDisconnected())) {\n            player.getInventory().placeItemBackInInventory(itemStack);\n        } else {\n            player.drop(itemStack, false);\n        }\n    }\n\n    public static interface IInterfaceRetriever {\n\n        /**\n         * Attempt to get a given interface instance.\n         * @param world The world.\n         * @param pos The position.\n         * @param clazz The class to find.\n         * @param <C> The class type.\n         * @return The optional instance.\n         */\n        public <C> Optional<C> getInterface(BlockGetter world, BlockPos pos, Class<C> clazz);\n\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T extends Exception, R> R sneakyThrow(Exception t) throws T {\n        throw (T) t;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/L10NValues.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport org.cyclops.integrateddynamics.api.APIReference;\n\n/**\n * Collection of generic L10N entries.\n * @author rubensworks\n */\npublic class L10NValues {\n\n    public static final String NS = APIReference.API_OWNER;\n\n    public static final String GENERAL_ENERGY_UNIT = \"general.\" + NS + \".energy_unit\";\n    public static final String GENERAL_ERROR_NONETWORK = \"general.\" + NS + \".error.no_network\";\n    public static final String GENERAL_ITEM_ID = \"item.\" + NS + \".general.id\";\n    public static final String GENERAL_TRUE = \"general.\" + NS + \".true\";\n    public static final String GENERAL_FALSE = \"general.\" + NS + \".false\";\n\n    public static final String PART_PANEL_ERROR_INVALIDTYPE = \"parttype.\" + NS + \".data_driven_panel.error.invalid_type\";\n    public static final String PART_ERROR_LOWENERGY = \"parttype.\" + NS + \".error.low_energy\";\n    public static final String PART_TOOLTIP_DISABLED = \"parttype.\" + NS + \".tooltip.disabled\";\n    public static final String PART_TOOLTIP_INACTIVE = \"parttype.\" + NS + \".tooltip.inactive\";\n    public static final String PART_TOOLTIP_ERRORS = \"parttype.\" + NS + \".tooltip.errors\";\n    public static final String PART_TOOLTIP_WRITER_ACTIVEASPECT = \"parttype.\" + NS + \".tooltip.writer.active_aspect\";\n    public static final String PART_TOOLTIP_DISPLAY_ACTIVEVALUE = \"parttype.\" + NS + \".tooltip.display.active_value\";\n    public static final String PART_TOOLTIP_MONODIRECTIONALCONNECTOR_GROUP = \"parttype.\" + NS + \".tooltip.monodirectionalconnector.group\";\n    public static final String PART_TOOLTIP_NOASPECTS = \"parttype.\" + NS + \".tooltip.noaspects\";\n    public static final String PART_TOOLTIP_MAXOFFSET = \"parttype.\" + NS + \".tooltip.maxoffset\";\n\n    public static final String GUI_RENAME = \"gui.\" + NS + \".button.rename\";\n    public static final String GUI_LOGICPROGRAMMER_FILTER = \"gui.\" + NS + \".logicprogrammer.filter\";\n    public static final String GUI_LOGICPROGRAMMER_RESET = \"gui.\" + NS + \".logicprogrammer.reset\";\n    public static final String GUI_LOGICPROGRAMMER_INFO_CREATE = \"gui.\" + NS + \".logicprogrammer.info.create\";\n    public static final String GUI_LOGICPROGRAMMER_INFO_MODIFY = \"gui.\" + NS + \".logicprogrammer.info.modify\";\n    public static final String GUI_LOGICPROGRAMMER_TOOLTIP_WRITESLOT_CREATE = \"gui.\" + NS + \".logicprogrammer.tooltip.writeslot.create\";\n    public static final String GUI_LOGICPROGRAMMER_TOOLTIP_WRITESLOT_MODIFY = \"gui.\" + NS + \".logicprogrammer.tooltip.writeslot.modify\";\n    public static final String GUI_INPUT = \"gui.\" + NS + \".input\";\n    public static final String GUI_OUTPUT = \"gui.\" + NS + \".output\";\n    public static final String GUI_RECIPE_STRICTNBT = \"gui.\" + NS + \".recipe.strictnbt\";\n    public static final String GUI_RECIPE_TAGVARIANTS = \"gui.\" + NS + \".recipe.tagvariants\";\n    public static final String GUI_RECIPE_REUSABLE = \"gui.\" + NS + \".recipe.reusable\";\n    public static final String GUI_OPERATOR_GLOBALNAME = \"gui.\" + NS + \".operator.globalname\";\n    public static final String GUI_OPERATOR_LOCALNAME = \"gui.\" + NS + \".operator.localname\";\n    // 0: value\n    public static final String GUI_MECHANICAL_SQUEEZER_TOGGLEFLUIDAUTOEJECT = \"gui.\" + NS + \".mechanical_squeezer.togglefluidautoeject\";\n\n    public static final String VALUE_ERROR = \"valuetype.\" + NS + \".error.value\";\n    public static final String VALUETYPE_VALUETYPE = \"valuetype.\" + NS + \".value_type\";\n    public static final String VALUETYPE_TOOLTIP_TYPENAME = \"valuetype.\" + NS + \".tooltip.type_name\";\n    public static final String VALUETYPE_TOOLTIP_VALUE = \"valuetype.\" + NS + \".tooltip.value\";\n    public static final String VALUETYPEOPERATOR_TOOLTIP_SIGNATURE = \"valuetype.\" + NS + \".operator.tooltip.signature\";\n    public static final String VALUETYPE_ERROR_INVALIDINPUT = \"valuetype.\" + NS + \".error.invalid_input\";\n    public static final String VALUETYPE_ERROR_INVALIDINPUTITEM = \"valuetype.\" + NS + \".error.invalid_input_item\";\n    public static final String VALUETYPE_ERROR_INVALIDLISTELEMENT = \"valuetype.\" + NS + \".error.invalid_list_element\";\n    // 0: value, 1: error\n    public static final String VALUETYPE_ERROR_DESERIALIZE = \"valuetype.\" + NS + \".error.deserialize\";\n    // 0: list value type, 1: given type\n    public static final String VALUETYPE_ERROR_INVALIDLISTVALUETYPE = \"valuetype.\" + NS + \".error.invalid_list_value_type\";\n    public static final String VALUETYPE_ERROR_INVALIDOPERATOROPERATOR = \"valuetype.\" + NS + \".error.invalid_operator_operator\";\n    public static final String VALUETYPE_ERROR_INVALIDOPERATORSIGNATURE = \"valuetype.\" + NS + \".error.invalid_operator_signature\";\n    public static final String VALUETYPE_OBJECT_BLOCK_ERROR_NOBLOCK = \"valuetype.\" + NS + \".error.block.no_block\";\n    public static final String VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID = \"valuetype.\" + NS + \".error.fluid.no_fluid\";\n    // 0: value type\n    public static final String VALUETYPE_ERROR_NOLIGHTCALCULATOR = \"valuetype.\" + NS + \".error.no_light_calculator\";\n\n    public static final String ASPECT_TOOLTIP_PARTID = \"aspect.\" + NS + \".tooltip.part_id\";\n    public static final String ASPECT_TOOLTIP_ASPECTNAME = \"aspect.\" + NS + \".tooltip.aspect_name\";\n    public static final String ASPECT_TOOLTIP_VALUETYPENAME = \"aspect.\" + NS + \".tooltip.value_type_name\";\n    public static final String ASPECT_ERROR_PARTNOTINNETWORK = \"variable.\" + NS + \".error.part_not_in_network\";\n    public static final String ASPECT_ERROR_INVALIDTYPE = \"aspect.\" + NS + \".error.invalid_type\";\n    public static final String ASPECT_ERROR_NOVALUEINTERFACE = \"aspect.\" + NS + \".error.no_value_interface\";\n    public static final String ASPECT_ERROR_NOVALUEINTERFACEVALUE = \"aspect.\" + NS + \".error.no_value_interface_value\";\n    public static final String ASPECT_ERROR_RECURSION = \"aspect.\" + NS + \".error.recursion\";\n\n    public static final String PROXY_TOOLTIP_PROXYID = \"proxy.\" + NS + \".tooltip.proxy_id\";\n    public static final String PROXY_ERROR_PROXYNOTINNETWORK = \"proxy.\" + NS + \".error.proxy_not_in_network\";\n    public static final String PROXY_ERROR_PROXYINVALID = \"proxy.\" + NS + \".error.proxy_invalid\";\n    public static final String PROXY_ERROR_PROXYINVALIDTYPE = \"proxy.\" + NS + \".error.proxy_invalid_type\";\n\n    public static final String DELAY_TOOLTIP_DELAYID = \"delay.\" + NS + \".tooltip.delay_id\";\n    public static final String DELAY_ERROR_DELAYNOTINNETWORK = \"delay.\" + NS + \".error.delay_not_in_network\";\n    public static final String DELAY_ERROR_DELAYINVALID = \"delay.\" + NS + \".error.delay_invalid\";\n    public static final String DELAY_ERROR_DELAYINVALIDTYPE = \"delay.\" + NS + \".error.delay_invalid_type\";\n\n    public static final String VARIABLE_ERROR_INVALIDITEM = \"variable.\" + NS + \".error.invalid_item\";\n    public static final String VARIABLE_ERROR_PARTNOTINNETWORK = \"variable.\" + NS + \".error.part_not_in_network\";\n    public static final String VARIABLE_ERROR_RECURSION = \"variable.\" + NS + \".error.recursion\";\n\n    public static final String OPERATOR_APPLIED_OPERATORNAME = \"operator.\" + NS + \".applied.operator_name\";\n    // 0: operator, 1: given-input, 2: position, 3: expected-input\n    public static final String OPERATOR_APPLIED_TYPE = \"operator.\" + NS + \".applied.type\";\n    public static final String OPERATOR_TOOLTIP_OPERATORNAME = \"operator.\" + NS + \".tooltip.operator_name\";\n    public static final String OPERATOR_TOOLTIP_OPERATORCATEGORY = \"operator.\" + NS + \".tooltip.operator_category\";\n    public static final String OPERATOR_TOOLTIP_INPUTTYPENAME = \"operator.\" + NS + \".tooltip.input_type_name\";\n    public static final String OPERATOR_TOOLTIP_OUTPUTTYPENAME = \"operator.\" + NS + \".tooltip.output_type_name\";\n    public static final String OPERATOR_TOOLTIP_VARIABLEIDS = \"operator.\" + NS + \".tooltip.variable_ids\";\n    public static final String OPERATOR_ERROR_VARIABLENOTINNETWORK = \"operator.\" + NS + \".error.variable_not_in_network\";\n    public static final String OPERATOR_ERROR_CYCLICREFERENCE = \"operator.\" + NS + \".error.cyclic_reference\";\n    // 0: operator, 1: given-length, 2: actual-length\n    public static final String OPERATOR_ERROR_WRONGINPUTLENGTH = \"operator.\" + NS + \".error.wrong_input_length\";\n    // 0: required-input-lengt,; 1: operator, 2: actual-input-length\n    public static final String OPERATOR_ERROR_OPERATORPARAMWRONGINPUTLENGTH = \"operator.\" + NS + \".error.operator_param_wrong_input_length\";\n    public static final String OPERATOR_ERROR_NULLTYPE = \"operator.\" + NS + \".error.null_type\";\n    // 0: operator, 1: given-input, 2: position, 3: expected-input\n    public static final String OPERATOR_ERROR_WRONGTYPE = \"operator.\" + NS + \".error.wrong_type\";\n    // 0: operator, 1: returned-input, 3: expected-output\n    public static final String OPERATOR_ERROR_WRONGTYPEOUTPUT = \"operator.\" + NS + \".error.wrong_type_output\";\n    public static final String OPERATOR_ERROR_WRONGCURRYINGTYPE = \"operator.\" + NS + \".error.wrong_currying_type\";\n    // 0: operator, 1: operator-inputs, 2: actual-inputs, 3: operator-output\n    public static final String OPERATOR_ERROR_CURRYINGOVERFLOW = \"operator.\" + NS + \".error.currying_overflow\";\n    // 0: operator, 1: given-output, 2: expected-output\n    public static final String OPERATOR_ERROR_WRONGPREDICATE = \"operator.\" + NS + \".error.wrong_predicate\";\n    // 0: expected-output, 1:given-output, 2:given-operator\n    public static final String OPERATOR_ERROR_ILLEGALPROPERY = \"operator.\" + NS + \".error.illegal_property\";\n    // 0: operator, 1: inner-operator, 2: given-length, 3: actual-length\n    public static final String OPERATOR_ERROR_WRONGINPUTLENGTHVIRTIUAL = \"operator.\" + NS + \".error.wrong_input_length_virtual\";\n    // 0: limit, 1: operator\n    public static final String OPERATOR_ERROR_RECURSIONLIMIT = \"operator.\" + NS + \".error.operator_recursion_limit\";\n    // 0: expression, 1: message\n    public static final String OPERATOR_ERROR_NBT_PATH_EXPRESSION = \"operator.\" + NS + \".error.operator_nbt_path_expression\";\n    // 0: value-type-from, 1: value-type-to\n    public static final String OPERATOR_ERROR_CAST_NOMAPPING = \"operator.\" + NS + \".error.cast.no_mapping\";\n    // 0: value-type-from, 1: value-type-to, 2: value\n    public static final String OPERATOR_ERROR_CAST_ILLEGAL = \"operator.\" + NS + \".error.cast.illegal\";\n    // 0: value-type-from-actual, 1: value-type-from-expected, 2: value-type-to\n    public static final String OPERATOR_ERROR_CAST_UNEXPECTED = \"operator.\" + NS + \".error.cast.unexpected\";\n    public static final String OPERATOR_ERROR_DIVIDEBYZERO = \"operator.\" + NS + \".error.divide_by_zero\";\n    // 0: string-value, 1: value-type\n    public static final String OPERATOR_ERROR_PARSE = \"operator.\" + NS + \".error.parse\";\n    // 0: value\n    public static final String OPERATOR_ERROR_NO_DESERIALIZER = \"operator.\" + NS + \".error.no_derserializer\";\n    // 0: regex-value\n    public static final String OPERATOR_ERROR_REGEX_INVALID = \"operator.\" + NS + \".error.regex.invalid\";\n    public static final String OPERATOR_ERROR_SUBSTRING_TOGREATERTHANFROM = \"operator.\" + NS + \".error.substring.to_greater_than_from\";\n    public static final String OPERATOR_ERROR_SUBSTRING_INDEXNEGATIVE = \"operator.\" + NS + \".error.substring.index_negative\";\n    public static final String OPERATOR_ERROR_SUBSTRING_LONGERTHANSTRING = \"operator.\" + NS + \".error.substring.longer_than_string\";\n    public static final String OPERATOR_ERROR_GROUP_INDEXNEGATIVE = \"operator.\" + NS + \".error.group.index_negative\";\n    // 0: regex-value, 1: value\n    public static final String OPERATOR_ERROR_GROUP_NOMATCH = \"operator.\" + NS + \".error.group.no_match\";\n    // 0: regex-value, 1: value, 2: group\n    public static final String OPERATOR_ERROR_GROUP_NOMATCHGROUP = \"operator.\" + NS + \".error.group.no_match_group\";\n    public static final String OPERATOR_ERROR_REGEXSCAN_INDEXNEGATIVE = \"operator.\" + NS + \".error.regex_scan.index_negative\";\n    // 0: regex-value, 1: value, 2: group\n    public static final String OPERATOR_ERROR_REGEXSCAN_NOMATCHGROUP = \"operator.\" + NS + \".error.regex_scan.no_match_group\";\n    // 0: replacement-value, 1: message\n    public static final String OPERATOR_ERROR_REPLACEREGEX_INVALIDGROUP = \"operator.\" + NS + \".error.replace_regex.invalid_group\";\n    // 0: operation\n    public static final String OPERATOR_ERROR_INFINITELIST_ILLEGAL = \"operator.\" + NS + \".error.infinite_list.illegal\";\n    // 0: index, 1: length\n    public static final String OPERATOR_ERROR_INDEXOUTOFBOUNDS = \"operator.\" + NS + \".error.index_out_of_bounds\";\n    public static final String OPERATOR_ERROR_SLICE_TOGREATERTHANFROM = \"operator.\" + NS + \".error.slice.to_greater_than_from\";\n    public static final String OPERATOR_ERROR_SLICE_INDEXNEGATIVE = \"operator.\" + NS + \".error.slice.index_negative\";\n    // 0: operator\n    public static final String OPERATOR_ERROR_OPERATORNOTFOUND = \"operator.\" + NS + \".error.operator_not_found\";\n    public static final String OPERATOR_ERROR_REDUCE_EMPTY = \"operator.\" + NS + \".error.reduce.empty\";\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/NbtHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.nbt.*;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\n\nimport java.util.Set;\n\n/**\n * @author rubensworks\n */\npublic class NbtHelpers {\n\n    /**\n     * Check if the first tag is a subset of the second tag.\n     * @param a An NBT tag.\n     * @param b An NBT tag.\n     * @param recursive If tags and list should be checked recursively. (list must be in the same order)\n     * @return If tag a is a subset (or equal) of tag b.\n     */\n    public static boolean nbtMatchesSubset(CompoundTag a, CompoundTag b, boolean recursive) {\n        for (String key : a.keySet()) {\n            Tag valueA = a.get(key);\n            if (recursive && (valueA instanceof CompoundTag || valueA instanceof ListTag)) {\n                Tag valueB = b.get(key);\n                if (valueA instanceof CompoundTag) {\n                    if (!(valueB instanceof CompoundTag)) {\n                        return false;\n                    }\n                    CompoundTag tagA = (CompoundTag) valueA;\n                    CompoundTag tagB = (CompoundTag) valueB;\n                    if (!nbtMatchesSubset(tagA, tagB, recursive)) {\n                        return false;\n                    }\n                } else if (valueA instanceof ListTag) {\n                    if (!(valueB instanceof ListTag)) {\n                        return false;\n                    }\n                    ListTag tagA = (ListTag) valueA;\n                    ListTag tagB = (ListTag) valueB;\n                    for (int i = 0; i < tagA.size(); i++) {\n                        CompoundTag subTagA = tagA.getCompound(i).orElseThrow();\n                        boolean foundA = false;\n                        for (int j = 0; j < tagB.size(); j++) {\n                            CompoundTag subTagB = tagB.getCompound(j).orElseThrow();\n                            if (nbtMatchesSubset(subTagA, subTagB, recursive)) {\n                                foundA = true;\n                                break;\n                            }\n                        }\n                        if (!foundA) {\n                            return false;\n                        }\n                    }\n                }\n            } else {\n                if (!valueA.equals(b.get(key))) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Create a new NBT tag that contains all entries from the given tags.\n     * If multiple tags contain the same entry, the entry from the latest tag will be given preference.\n     * If nested tags are present, these will be combined recursively.\n     * @param tags NBT tags.\n     * @return A new tag containing the combined entries from the given tags.\n     */\n    public static CompoundTag union(CompoundTag... tags) {\n        CompoundTag tag = new CompoundTag();\n        for (CompoundTag inputTag : tags) {\n            tag.merge(inputTag);\n        }\n        return tag;\n    }\n\n    /**\n     * Create a new NBT tag that contains the entries that are present in all given tags.\n     * If nested tags are present, these will be intersected recursively.\n     * @param tags NBT tags.\n     * @return A new tag containing the intersected entries from the given tags.\n     */\n    public static CompoundTag intersection(CompoundTag... tags) {\n        if (tags.length == 0) {\n            return new CompoundTag();\n        }\n        System.out.println(tags[0]);\n        System.out.println(tags[1].toString());\n        CompoundTag tag = null;\n        for (CompoundTag inputTag : tags) {\n            if (tag == null) {\n                tag = inputTag.copy();\n            } else {\n                Set<String> keys = Sets.newHashSet(tag.keySet());\n                for (String key : keys) {\n                    int type = tag.get(key).getId();\n                    if (!inputTag.contains(key)) {\n                        tag.remove(key);\n                    } else if (type == Tag.TAG_COMPOUND) {\n                        tag.put(key, intersection(tag.getCompound(key).orElseThrow(), inputTag.getCompound(key).orElseThrow()));\n                    }\n                }\n            }\n        }\n        return tag;\n    }\n\n    /**\n     * Create a new NBT tag that contains all entries of the first tag minus the entries of the second tag.\n     * If nested tags are present, these will be operated recursively.\n     * @param a an NBT tag.\n     * @param b an NBT tag.\n     * @return A new tag containing the entries of a minus b.\n     */\n    public static CompoundTag minus(CompoundTag a, CompoundTag b) {\n        CompoundTag tag = a.copy();\n        for (String key : b.keySet()) {\n            int type = b.get(key).getId();\n            if (tag.contains(key)) {\n                if (type == Tag.TAG_COMPOUND) {\n                    CompoundTag difference = minus(tag.getCompound(key).orElseThrow(), b.getCompound(key).orElseThrow());\n                    if (difference.isEmpty()) {\n                        tag.remove(key);\n                    } else {\n                        tag.put(key, difference);\n                    }\n                } else {\n                    tag.remove(key);\n                }\n            }\n        }\n        return tag;\n    }\n\n    /**\n     * Create an NBT List from the given NBT value list.\n     * @param value An NBT value list.\n     * @param operatorName An operator name for error reporting.\n     * @return An NBT list.\n     */\n    public static ListTag getListNbtTag(ValueTypeList.ValueList<?, ?> value, Component operatorName) {\n        ListTag list = new ListTag();\n        for (IValue valueNbt : value.getRawValue()) {\n            if (!(value.getRawValue().getValueType() == ValueTypes.NBT ||\n                    (value.getRawValue().getValueType() == ValueTypes.CATEGORY_ANY && valueNbt.getType() == ValueTypes.NBT))) {\n                MutableComponent error = Component.translatable(\n                        L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        operatorName,\n                        Component.translatable(value.getRawValue().getValueType().getTranslationKey()),\n                        1,\n                        Component.translatable(ValueTypes.NBT.getTranslationKey()));\n                Helpers.sneakyThrow(new EvaluationException(error));\n            }\n            ((ValueTypeNbt.ValueNbt) valueNbt).getRawValue().ifPresent(list::add);\n        }\n        return list;\n    }\n\n    /**\n     * Create an NBT byte array from the given integer value list.\n     * @param value An integer value list.\n     * @param operatorName An operator name for error reporting.\n     * @return An NBT byte array.\n     */\n    public static ByteArrayTag getListNbtByte(ValueTypeList.ValueList<?, ?> value, Component operatorName) {\n        byte[] array = null;\n        try {\n            array = new byte[value.getRawValue().getLength()];\n        } catch (EvaluationException error) {\n            Helpers.sneakyThrow(error);\n        }\n        int index = 0;\n        for (IValue valueNbt : value.getRawValue()) {\n            if (valueNbt.getType() != ValueTypes.INTEGER) {\n                MutableComponent error = Component.translatable(\n                        L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        operatorName,\n                        Component.translatable(valueNbt.getType().getTranslationKey()),\n                        1,\n                        Component.translatable(ValueTypes.INTEGER.getTranslationKey()));\n                Helpers.sneakyThrow(new EvaluationException(error));\n            }\n            array[index++] = (byte) ((ValueTypeInteger.ValueInteger) valueNbt).getRawValue();\n        }\n        return new ByteArrayTag(array);\n    }\n\n    /**\n     * Create an NBT int array from the given integer value list.\n     * @param value An integer value list.\n     * @param operatorName An operator name for error reporting.\n     * @return An NBT int array.\n     */\n    public static IntArrayTag getListNbtInt(ValueTypeList.ValueList<?, ?> value, Component operatorName) {\n        int[] array = null;\n        try {\n            array = new int[value.getRawValue().getLength()];\n        } catch (EvaluationException error) {\n            Helpers.sneakyThrow(error);\n        }\n        int index = 0;\n        for (IValue valueNbt : value.getRawValue()) {\n            if (valueNbt.getType() != ValueTypes.INTEGER) {\n                MutableComponent error = Component.translatable(\n                        L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        operatorName,\n                        Component.translatable(valueNbt.getType().getTranslationKey()),\n                        1,\n                        Component.translatable(ValueTypes.INTEGER.getTranslationKey()));\n                Helpers.sneakyThrow(new EvaluationException(error));\n            }\n            array[index++] = ((ValueTypeInteger.ValueInteger) valueNbt).getRawValue();\n        }\n        return new IntArrayTag(array);\n    }\n\n    /**\n     * Create an NBT long array from the given long value list.\n     * @param value A long value list.\n     * @param operatorName An operator name for error reporting.\n     * @return An NBT long list.\n     */\n    public static LongArrayTag getListNbtLong(ValueTypeList.ValueList<?, ?> value, Component operatorName) {\n        long[] array = null;\n        try {\n            array = new long[value.getRawValue().getLength()];\n        } catch (EvaluationException error) {\n            Helpers.sneakyThrow(error);\n        }\n        int index = 0;\n        for (IValue valueNbt : value.getRawValue()) {\n            if (valueNbt.getType() != ValueTypes.LONG) {\n                MutableComponent error = Component.translatable(\n                        L10NValues.OPERATOR_ERROR_WRONGTYPE,\n                        operatorName,\n                        Component.translatable(valueNbt.getType().getTranslationKey()),\n                        1,\n                        Component.translatable(ValueTypes.LONG.getTranslationKey()));\n                Helpers.sneakyThrow(new EvaluationException(error));\n            }\n            array[index++] = ((ValueTypeLong.ValueLong) valueNbt).getRawValue();\n        }\n        return new LongArrayTag(array);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/NetworkHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.ScheduledTickAccess;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.redstone.Orientation;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.network.*;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.capability.path.SidedPathElement;\nimport org.cyclops.integrateddynamics.core.TickHandler;\nimport org.cyclops.integrateddynamics.core.network.Network;\nimport org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Network helper methods.\n * @author rubensworks\n */\npublic class NetworkHelpers {\n\n    /**\n     * Get the network carrier capability at the given position.\n     * If possible, prefer using the variant with block state.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional network carrier capability.\n     */\n    @Deprecated\n    public static Optional<INetworkCarrier> getNetworkCarrier(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.NetworkCarrier.BLOCK);\n    }\n\n    /**\n     * Get the network carrier capability at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @param blockState The block state.\n     * @return The optional network carrier capability.\n     */\n    public static Optional<INetworkCarrier> getNetworkCarrier(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.NetworkCarrier.BLOCK, pos, blockState, null, side));\n    }\n\n    /**\n     * Get the network element provider capability at the given position.\n     * If possible, prefer using the variant with block state.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional network element provider capability.\n     */\n    @Deprecated\n    public static Optional<INetworkElementProvider> getNetworkElementProvider(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.NetworkElementProvider.BLOCK);\n    }\n\n    /**\n     * Get the network element provider capability at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @param blockState The block state.\n     * @return The optional network element provider capability.\n     */\n    public static Optional<INetworkElementProvider> getNetworkElementProvider(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.NetworkElementProvider.BLOCK, pos, blockState, null, side));\n    }\n\n    /**\n     * Get the network at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional network.\n     */\n    public static Optional<INetwork> getNetwork(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        Optional<Optional<INetwork>> networkCarried = getNetworkCarrier(world, pos, side)\n                .map(carrier -> {\n                    INetwork network = carrier.getNetwork();\n                    return network != null ? Optional.of(network) : Optional.empty();\n                });\n        return networkCarried.orElse(Optional.empty());\n    }\n\n    /**\n     * Get the network at the given position.\n     * @param pos The position.\n     * @return The optional network.\n     */\n    public static Optional<INetwork> getNetwork(PartPos pos) {\n        return getNetwork(pos.getPos().getLevel(true), pos.getPos().getBlockPos(), pos.getSide());\n    }\n\n    /**\n     * Get the network at the given position.\n     * If it is not present, then an illegal state exception will be thrown.\n     *\n     * This should only be called if you know for certain that there will be a network present.\n     *\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The network.\n     */\n    public static INetwork getNetworkChecked(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return getNetwork(world, pos, side)\n                .orElseThrow(() -> new IllegalStateException(\"Could not find a network container at \" + pos.toString()));\n    }\n\n    /**\n     * Get the network at the given position.\n     * If it is not present, then an illegal state exception will be thrown.\n     *\n     * This should only be called if you know for certain that there will be a network present.\n     *\n     * @param pos The position.\n     * @return The network.\n     */\n    public static INetwork getNetworkChecked(PartPos pos) {\n        return getNetwork(pos)\n                .orElseThrow(() -> new IllegalStateException(\"Could not find a network container at \" + pos.toString()));\n    }\n\n    /**\n     * Get the part network capability of a network.\n     * @param optionalNetwork The optional network.\n     * @return The optional part network.\n     */\n    public static Optional<IPartNetwork> getPartNetwork(Optional<INetwork> optionalNetwork) {\n        return optionalNetwork\n                .map(network -> network.getCapability(Capabilities.PartNetwork.NETWORK))\n                .orElse(Optional.empty());\n    }\n\n    /**\n     * Get the part network capability of a network.\n     * @param network The network.\n     * @return The optional part network.\n     */\n    public static Optional<IPartNetwork> getPartNetwork(@Nullable INetwork network) {\n        if (network == null) {\n            return Optional.empty();\n        }\n        return network.getCapability(Capabilities.PartNetwork.NETWORK);\n    }\n\n    /**\n     * Get the part network capability of a network.\n     * If it is not present, then an illegal state exception will be thrown.\n     *\n     * This should only be called if you know for certain that there will be a part network present.\n     *\n     * @param network The network.\n     * @return The part network.\n     */\n    public static IPartNetwork getPartNetworkChecked(INetwork network) {\n        return network.getCapability(Capabilities.PartNetwork.NETWORK)\n                .orElseThrow(() -> new IllegalStateException(\"Could not find a network's part network\"));\n    }\n\n    /**\n     * Get the part network capability of a network.\n     * @param optionalNetwork The optional network.\n     * @return The optional energy network.\n     */\n    public static Optional<IEnergyNetwork> getEnergyNetwork(Optional<INetwork> optionalNetwork) {\n        return optionalNetwork\n                .map(network -> network.getCapability(Capabilities.EnergyNetwork.NETWORK))\n                .orElse(Optional.empty());\n    }\n\n    /**\n     * Get the part network capability of a network.\n     * @param network The network.\n     * @return The optional energy network.\n     */\n    public static Optional<IEnergyNetwork> getEnergyNetwork(@Nullable INetwork network) {\n        if (network == null) {\n            return Optional.empty();\n        }\n        return network.getCapability(Capabilities.EnergyNetwork.NETWORK);\n    }\n\n    /**\n     * Get the part network capability of a network.\n     * @param network The network.\n     * @return The energy network.\n     */\n    public static IEnergyNetwork getEnergyNetworkChecked(INetwork network) {\n        return network.getCapability(Capabilities.EnergyNetwork.NETWORK)\n                .orElseThrow(() -> new IllegalStateException(\"Could not find a network's energy network\"));\n    }\n\n    /**\n     * Get the ingredient network within a network.\n     * @param optionalNetwork The optional network.\n     * @param ingredientComponent The ingredient component type.\n     * @param <T> The instance type.\n     * @param <M> The matching condition parameter.\n     * @return The optional ingredient network.\n     */\n    public static <T, M> Optional<IPositionedAddonsNetworkIngredients<T, M>> getIngredientNetwork(Optional<INetwork> optionalNetwork,\n                                                                                                      IngredientComponent<T, M> ingredientComponent) {\n        return optionalNetwork\n                .map(network -> ingredientComponent.getCapability(Capabilities.PositionedAddonsNetworkIngredientsHandler.INGREDIENT)\n                        .map(handler -> handler.getStorage(network))\n                        .orElse(Optional.empty()))\n                .orElse(Optional.empty());\n    }\n\n    /**\n     * Form a new network starting from the given position.\n     * This position should have a {@link IPathElement} capability,\n     * otherwise this method will fail silently.\n     * This will correctly transfer all passed network elements to this new network.\n     * @param world The world.\n     * @param pos The starting position.\n     * @param side The side.\n     * @return The optionally created part network.\n     * Can be absent if the starting position did not have a {@link IPathElement} capability.\n     */\n    public static Optional<INetwork> initNetwork(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.PathElement.BLOCK)\n                .map(pathElement -> {\n                    Network network = Network.initiateNetworkSetup(SidedPathElement.of(pathElement, side));\n                    network.initialize();\n                    return Optional.<INetwork>of(network);\n                })\n                .orElse(Optional.empty());\n    }\n\n    /**\n     * This MUST be called by blocks having the {@link INetworkElementProvider} capability in\n     * when a neighbouring block is updated, more specifically when\n     * {@link Block#neighborChanged(BlockState, Level, BlockPos, Block, Orientation, boolean)},\n     * {@link Block#onNeighborChange(BlockState, LevelReader, BlockPos, BlockPos)}\n     * or {@link Block#updateShape(BlockState, LevelReader, ScheduledTickAccess, BlockPos, Direction, BlockPos, BlockState, RandomSource)} is called.\n     *\n     * @param world The world in which the neighbour was updated.\n     * @param pos   The position of the center block.\n     * @param side  The side at the center block.\n     */\n    public static void onElementProviderBlockNeighborChange(Level world, BlockPos pos,\n                                                            @Nullable Direction side) {\n        if (!world.isClientSide()) {\n            getNetwork(world, pos, side).ifPresent(network -> {\n                getNetworkElementProvider(world, pos, side).ifPresent(networkElementProvider -> {\n                    for (INetworkElement networkElement : networkElementProvider.createNetworkElements(world, pos)) {\n                        networkElement.onNeighborBlockChange(network, world, side);\n                    }\n                });\n            });\n        }\n    }\n\n    /**\n     * @return If networks should work and evaluations should be done.\n     */\n    public static boolean shouldWork() {\n        return !GeneralConfig.safeMode;\n    }\n\n    /**\n     * Invalidate all network elements at the given position.\n     * Warning: this assumes unsided network carrier capabilities, for example full-block network elements.\n     * @param world The world.\n     * @param pos The position.\n     * @param network The network.\n     * @param networkElementProvider The network element provider.\n     */\n    public static void invalidateNetworkElements(Level world, BlockPos pos, INetwork network, INetworkElementProvider networkElementProvider) {\n        for (INetworkElement networkElement : networkElementProvider.createNetworkElements(world, pos)) {\n            networkElement.invalidate(network);\n        }\n    }\n\n    /**\n     * Revalidate all network elements at the given position.\n     * Warning: this assumes unsided network carrier capabilities, for example full-block network elements.\n     * @param world The world.\n     * @param pos The position.\n     * @return If the network was revalidated in this tick.\n     */\n    public static boolean revalidateNetworkElements(Level world, BlockPos pos) {\n        INetworkCarrier networkCarrier = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, Capabilities.NetworkCarrier.BLOCK).orElse(null);\n        IPathElement pathElement = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, Capabilities.PathElement.BLOCK).orElse(null);\n        if (TickHandler.getInstance().ticked\n                && networkCarrier != null && pathElement != null && networkCarrier.getNetwork() == null\n                && IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, Capabilities.CableFakeable.BLOCK).map(ICableFakeable::isRealCable).orElse(true)) {\n            return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, Capabilities.NetworkElementProvider.BLOCK).map(networkElementProvider -> {\n                // Attempt to revalidate the network elements in this provider\n                boolean foundNetwork = false;\n                for (INetwork network : NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getNetworks()) {\n                    if (network.containsSidedPathElement(SidedPathElement.of(pathElement, null))) {\n                        // Revalidate all network elements\n                        for (INetworkElement networkElement : networkElementProvider.createNetworkElements(world, pos)) {\n                            networkElement.revalidate(network);\n                        }\n                        foundNetwork = true;\n                        break; // No need to check the other networks anymore\n                    }\n                }\n\n                // If no existing network was found, create a new network\n                if (!foundNetwork && GeneralConfig.recreateCorruptedNetworks) {\n                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"Detected network position at \" +\n                            \"position %s in world %s with corrupted network, recreating network...\", pos, world.dimension().identifier()));\n                    NetworkHelpers.initNetwork(world, pos, null);\n                    return true;\n                }\n\n                return foundNetwork;\n            }).orElse(false);\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/PartHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport com.google.common.collect.ImmutableMap;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.player.Player;\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.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.common.extensions.ILevelExtension;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.network.event.UnknownPartEvent;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\nimport org.cyclops.integrateddynamics.core.part.PartTypeRegistry;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Helpers related to parts.\n * @author rubensworks\n */\npublic class PartHelpers {\n\n    /**\n     * Get the part container capability at the given position.\n     * If possible, prefer using the variant with block state.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The optional part container capability.\n     */\n    @Deprecated\n    public static Optional<IPartContainer> getPartContainer(ILevelExtension world, BlockPos pos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.PartContainer.BLOCK);\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @param blockState The block state.\n     * @return The optional part container capability.\n     */\n    public static Optional<IPartContainer> getPartContainer(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {\n        return Optional.ofNullable(world.getCapability(Capabilities.PartContainer.BLOCK, pos, blockState, null, side));\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     * If possible, prefer using the variant with block state.\n     * @param dimPos The dimensional position.\n     * @param side The side.\n     * @return The optional part container capability.\n     */\n    @Deprecated\n    public static Optional<IPartContainer> getPartContainer(DimPos dimPos, @Nullable Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos, side, Capabilities.PartContainer.BLOCK);\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     *\n     * @param dimPos      The dimensional position.\n     * @param side        The side.\n     * @param blockState  The block state.\n     * @param blockEntity The block entity.\n     * @return The optional part container capability.\n     */\n    public static Optional<IPartContainer> getPartContainer(DimPos dimPos, @Nullable Direction side, @Nullable BlockState blockState, @Nullable BlockEntity blockEntity) {\n        Level level = dimPos.getLevel(true);\n        if (level == null) {\n            return Optional.empty();\n        }\n        return Optional.ofNullable(level.getCapability(Capabilities.PartContainer.BLOCK, dimPos.getBlockPos(), blockState, blockEntity, side));\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     * If it is not present, then an illegal state exception will be thrown.\n     *\n     * This should only be called if you know for certain that there will be a part container present.\n     *\n     * @param world The world.\n     * @param pos The position.\n     * @param side The side.\n     * @return The part container capability.\n     */\n    public static IPartContainer getPartContainerChecked(Level world, BlockPos pos, @Nullable Direction side) {\n        return getPartContainer(world, pos, side)\n                .orElseThrow(() -> new PartStateException(DimPos.of(world, pos), side));\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     * If it is not present, then an illegal state exception will be thrown.\n     *\n     * This should only be called if you know for certain that there will be a part container present.\n     *\n     * @param dimPos The dimensional position.\n     * @param side The side.\n     * @return The part container capability.\n     */\n    public static IPartContainer getPartContainerChecked(DimPos dimPos, @Nullable Direction side) {\n       return PartHelpers.getPartContainer(dimPos, side)\n               .orElseThrow(() -> new PartStateException(dimPos, side));\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     * If it is not present, then an illegal state exception will be thrown.\n     * <p>\n     * This should only be called if you know for certain that there will be a part container present.\n     *\n     * @param dimPos      The dimensional position.\n     * @param side        The side.\n     * @param blockState  The block state.\n     * @param blockEntity The block entity.\n     * @return The part container capability.\n     */\n    public static IPartContainer getPartContainerChecked(DimPos dimPos, @Nullable Direction side, @Nullable BlockState blockState, @Nullable BlockEntity blockEntity) {\n        return PartHelpers.getPartContainer(dimPos, side, blockState, blockEntity)\n                .orElseThrow(() -> new PartStateException(dimPos, side));\n    }\n\n    /**\n     * Get the part container capability at the given position.\n     * If it is not present, then an illegal state exception will be thrown.\n     *\n     * This should only be called if you know for certain that there will be a part container present.\n     *\n     * @param pos The part position.\n     * @return The part container capability.\n     */\n    public static IPartContainer getPartContainerChecked(PartPos pos) {\n        return PartHelpers.getPartContainerChecked(pos.getPos(), pos.getSide());\n    }\n\n    /**\n     * Check if the given part type is null and run it through the network even bus in an {@link UnknownPartEvent}\n     * to get another type.\n     * @param network The network.\n     * @param partTypeName The part name.\n     * @param partType The part type.\n     * @return A possibly non-null part type.\n     */\n    public static IPartType validatePartType(INetwork network, String partTypeName, @Nullable IPartType partType) {\n        if(network != null && partType == null) {\n            UnknownPartEvent event = new UnknownPartEvent(network, partTypeName);\n            network.getEventBus().post(event);\n            partType = event.getPartType();\n        }\n        return partType;\n    }\n\n    /**\n     * Write the given part type to nbt.\n     * @param valueOutput The output to write to.\n     * @param side The side to write.\n     * @param partType The part type to write.\n     */\n    public static void serializePartType(ValueOutput valueOutput, Direction side, IPartType partType) {\n        valueOutput.putString(\"__partType\", partType.getUniqueName().toString());\n        valueOutput.putString(\"__side\", side.getSerializedName());\n    }\n\n    /**\n     * Write the given part data to nbt.\n     * @param valueOutput The output to write to.\n     * @param pos The position of the part, used for error reporting.\n     * @param partData The part data.\n     * @return If the writing succeeded.\n     */\n    public static boolean serializePart(ValueOutput valueOutput, BlockPos pos, Pair<Direction, PartStateHolder<?, ?>> partData) {\n        IPartType part = partData.getValue().getPart();\n        IPartState partState = partData.getValue().getState();\n        serializePartType(valueOutput, partData.getKey(), part);\n        try {\n            part.serializeState(valueOutput, partState);\n            return true;\n        } catch (Exception e) {\n            e.printStackTrace();\n            IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR,  String.format(\"The part %s at position %s was errored \" +\n                    \"and is removed.\", part.getUniqueName(), pos));\n            return false;\n        }\n    }\n\n    /**\n     * Write the given parts to nbt.\n     * @param valueOutput The output to write to.\n     * @param pos The position of the part, used for error reporting.\n     * @param partData The part data.\n     */\n    public static void serializeParts(ValueOutput valueOutput, BlockPos pos, Map<Direction, PartStateHolder<?, ?>> partData) {\n        ValueOutput.ValueOutputList partList = valueOutput.childrenList(\"parts\");\n        for(Map.Entry<Direction, PartHelpers.PartStateHolder<?, ?>> entry : partData.entrySet()) {\n            if(!serializePart(partList.addChild(), pos, Pair.of(entry.getKey(), entry.getValue()))) {\n                partList.discardLast();\n            }\n        }\n    }\n\n    /**\n     * Read a part from nbt.\n     * @param valueInput The value to read from.\n     * @param network The network the part will be part of.\n     * @param pos The position of the part, used for error reporting.\n     * @return The part data.\n     */\n    public static Pair<Direction, IPartType> deserializePartType(ValueInput valueInput, @Nullable INetwork network, BlockPos pos) {\n        String partTypeName = valueInput.getString(\"__partType\").orElseThrow();\n        IPartType partType = validatePartType(network, partTypeName, PartTypes.REGISTRY.getPartType(Identifier.parse(partTypeName)));\n        if(partType != null) {\n            Direction side = Direction.byName(valueInput.getString(\"__side\").orElseThrow());\n            if (side != null) {\n                return Pair.of(side, partType);\n            } else {\n                IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"The part %s at position %s was at an invalid \" +\n                                \"side and removed.\",\n                        partType.getUniqueName(), pos));\n            }\n        } else {\n            IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"The part %s at position %s was unknown and removed.\",\n                    partTypeName, pos));\n        }\n        return null;\n    }\n\n    /**\n     * Read part data from nbt.\n     * @param valueInput The value to read from.\n     * @param network The network the part will be part of.\n     * @param pos The position of the part, used for error reporting.\n     * @return The part data.\n     */\n    public static Pair<Direction, ? extends PartStateHolder<?, ?>> deserializePart(ValueInput valueInput, @Nullable INetwork network, BlockPos pos) {\n        Pair<Direction, IPartType> partData = deserializePartType(valueInput, network, pos);\n        if(partData != null) {\n            IPartState partState = partData.getValue().deserializeState(valueInput);\n            return Pair.of(partData.getKey(), PartStateHolder.of(partData.getValue(), partState));\n        }\n        return null;\n    }\n\n    /**\n     * Read parts data from nbt.\n     * If the world is not null and we are running client-side,\n     * a block render update will automatically be triggered if needed.\n     * @param valueInput The value to read from.\n     * @param network The network the part will be part of.\n     * @param pos The position of the part, used for error reporting.\n     * @param partData The map of part data to write to.\n     * @param world The world.\n     */\n    public static void deserializeParts(ValueInput valueInput, @Nullable INetwork network, BlockPos pos,\n                                        Map<Direction, PartStateHolder<?, ?>> partData, Level world) {\n        Map<Direction, PartStateHolder<?, ?>> oldPartData = ImmutableMap.copyOf(partData);\n        partData.clear();\n        for (ValueInput partTag : valueInput.childrenList(\"parts\").orElseThrow()) {\n            Pair<Direction, ? extends PartStateHolder<?, ?>> part = deserializePart(partTag, network, pos);\n            if(part != null) {\n                partData.put(part.getKey(), part.getValue());\n            }\n        }\n\n        // Trigger block render update if at least one of the parts requires it.\n        if (world != null && world.isClientSide()) {\n            boolean triggerBlockRenderUpdate = false;\n            for (Direction side : Direction.values()) {\n                PartStateHolder<?, ?> oldData = oldPartData.get(side);\n                PartStateHolder<?, ?> newData = partData.get(side);\n                if (oldData != null || newData != null) {\n                    IPartType oldPartType = oldData != null ? oldData.getPart() : null;\n                    IPartType newPartType = newData != null ? newData.getPart() : null;\n                    IPartState oldPartState = oldData != null ? oldData.getState() : null;\n                    IPartState newPartState = newData != null ? newData.getState() : null;\n\n                    if (oldPartType != newPartType\n                            || oldPartType.shouldTriggerBlockRenderUpdate(oldPartState, newPartState)) {\n                        triggerBlockRenderUpdate = true;\n                        break;\n                    }\n                }\n            }\n            if (triggerBlockRenderUpdate) {\n                IModHelpers.get().getBlockHelpers().markForUpdate(world, pos);\n            }\n        }\n    }\n\n    /**\n     * Remove a part from the given side of the given part container.\n     * @param world The world.\n     * @param pos The position of the container.\n     * @param side The side.\n     * @param player The player that is removing the part or null.\n     * @param destroyIfEmpty If the cable block must be removed if no other parts are present after this removal.\n     * @param dropMainElement If the main part element should be dropped.\n     * @param saveState If the element state should be saved in the item.\n     * @return If the block was set to air (removed).\n     */\n    public static boolean removePart(Level world, BlockPos pos, Direction side, @Nullable Player player,\n                                     boolean destroyIfEmpty, boolean dropMainElement, boolean saveState) {\n        IPartContainer partContainer = getPartContainerChecked(world, pos, side);\n        ICableFakeable cableFakeable = CableHelpers.getCableFakeable(world, pos, side).orElse(null);\n        partContainer.removePart(side, player, dropMainElement, saveState);\n\n        // Remove full cable block if this was the last part and if it was already an unreal cable.\n        boolean removeCompletely = destroyIfEmpty && (cableFakeable == null || !cableFakeable.isRealCable()) && !partContainer.hasParts();\n        if(removeCompletely) {\n            world.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);\n        } else {\n            world.updateNeighborsAt(pos, world.getBlockState(pos).getBlock());\n            // If there is a cable in the direction of the removed part, try connecting with it.\n            if (CableHelpers.getCable(world, pos.relative(side), side.getOpposite()).isPresent()) {\n                CableHelpers.updateConnections(world, pos, side);\n                CableHelpers.updateConnections(world, pos.relative(side), side.getOpposite());\n                NetworkHelpers.initNetwork(world, pos, side);\n            }\n        }\n\n        return !removeCompletely;\n    }\n\n    /**\n     * Add a part to the given side with the part state in the given item.\n     * @param level The world.\n     * @param pos The position of the container.\n     * @param side The side.\n     * @param partType The part type.\n     * @param itemStack The item holding the part state.\n     * @return If the part was added.\n     */\n    public static boolean addPart(Level level, BlockPos pos, Direction side, IPartType partType, ItemStack itemStack) {\n        IPartContainer partContainer = getPartContainerChecked(level, pos, side);\n        if(partContainer.canAddPart(side, partType)) {\n            if(!level.isClientSide()) {\n                partContainer.setPart(side, partType, partType.getState(ValueDeseralizationContext.of(level), new PartPathElement(pos), itemStack));\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Add a part to the given side with the part state.\n     * @param world The world.\n     * @param pos The position of the container.\n     * @param side The side.\n     * @param partType The part type.\n     * @param partState The part state.\n     * @return If the part was added.\n     */\n    public static boolean addPart(Level world, BlockPos pos, Direction side, IPartType partType, IPartState partState) {\n        IPartContainer partContainer = getPartContainerChecked(world, pos, side);\n        if(partContainer.canAddPart(side, partType)) {\n            if(!world.isClientSide()) {\n                partContainer.setPart(side, partType, partState);\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Forcefully set a part at the given side.\n     * @param network The network.\n     * @param world The world.\n     * @param pos The position of the container.\n     * @param side The side.\n     * @param part The part to set.\n     * @param partState The part state to set.\n     * @param callback The callback for the part state holder.\n     * @return If the part could be placed.\n     */\n    public static boolean setPart(@Nullable INetwork network, Level world, BlockPos pos, Direction side, IPartType part, IPartState partState, IPartStateHolderCallback callback) {\n        callback.onSet(PartStateHolder.of(part, partState));\n        if(network != null) {\n            IPartContainer partContainer = PartHelpers.getPartContainerChecked(world, pos, side);\n            INetworkElement networkElement = part.createNetworkElement(partContainer, DimPos.of(world, pos), side);\n            if(!network.addNetworkElement(networkElement, false)) {\n                // In this case, the addition failed because that part id is already present in the network,\n                // therefore we have to make a new state for that part (with a new id) and retry.\n                partState = part.defaultBlockState();\n                callback.onSet(PartStateHolder.of(part, partState));\n                IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, \"A part already existed in the network, this is possibly a \" +\n                        \"result from item duplication.\");\n                network.addNetworkElement(networkElement, false);\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * If the given player can currently interact with the part gui at the given position.\n     * @param target The part target.\n     * @param player The player.\n     * @param expectedPartContainer The expected part container.\n     * @return If the player can interact with it.\n     */\n    public static boolean canInteractWith(PartTarget target, Player player, IPartContainer expectedPartContainer) {\n        IPartContainer partContainer = PartHelpers.getPartContainer(target.getCenter().getPos(), target.getCenter().getSide()).orElse(null);\n        return partContainer == expectedPartContainer;\n    }\n\n    /**\n     * Get a part at the given position.\n     * @param partPos The part position.\n     * @return The part.\n     */\n    public static @Nullable PartStateHolder<?, ?> getPart(PartPos partPos) {\n        Direction side = partPos.getSide();\n        IPartContainer partContainer = PartHelpers.getPartContainer(partPos.getPos(), partPos.getSide()).orElse(null);\n        if (partContainer != null && partContainer.hasPart(side)) {\n            return PartStateHolder.of(partContainer.getPart(side), partContainer.getPartState(side));\n        }\n        return null;\n    }\n\n    /**\n     * Open a part gui container from the server.\n     * @param player The player opening the gui.\n     * @param pos The part position.\n     * @param partType The part type.\n     * @return The action result.\n     */\n    public static InteractionResult openContainerPart(ServerPlayer player, PartPos pos, IPartType<?, ?> partType) {\n        return partType.getContainerProvider(pos)\n                .<InteractionResult>map(containerProvider -> {\n                    player.openMenu(containerProvider, packetBuffer -> partType.writeExtraGuiData(packetBuffer, pos, player));\n                    return InteractionResult.SUCCESS;\n                })\n                .orElse(InteractionResult.PASS);\n    }\n\n    /**\n     * Open a part settings gui container from the server.\n     * @param player The player opening the gui.\n     * @param pos The part position.\n     * @param partType The part type.\n     * @return If the part has a container provider for settings.\n     */\n    public static boolean openContainerPartSettings(ServerPlayer player, PartPos pos, IPartType<?, ?> partType) {\n        return partType.getContainerProviderSettings(pos)\n                .map(containerProvider -> {\n                    player.openMenu(containerProvider, packetBuffer -> partType.writeExtraGuiDataSettings(packetBuffer, pos, player));\n                    return true;\n                })\n                .orElse(false);\n    }\n\n    /**\n     * Open an aspect settings gui container from the server.\n     * @param player The player opening the gui.\n     * @param pos The part position.\n     * @param aspect The aspect for which to show the settings.\n     */\n    public static void openContainerAspectSettings(ServerPlayer player, PartPos pos, IAspect<?, ?> aspect) {\n        player.openMenu(aspect.getPropertiesContainerProvider(pos),\n                packetBuffer -> packetBuffer.writeUtf(aspect.getUniqueName().toString()));\n    }\n\n    /**\n     * Open a part offset gui container from the server.\n     * @param player The player opening the gui.\n     * @param pos The part position.\n     * @param partType The part type.\n     * @return If the part has a container provider for offsets.\n     */\n    public static boolean openContainerPartOffsets(ServerPlayer player, PartPos pos, IPartType<?, ?> partType) {\n        return partType.getContainerProviderOffsets(pos)\n                .map(containerProvider -> {\n                    player.openMenu(containerProvider, packetBuffer -> partType.writeExtraGuiDataOffsets(packetBuffer, pos, player));\n                    return true;\n                })\n                .orElse(false);\n    }\n\n    /**\n     * Construct a data holder for constructing a part-related container.\n     * @param pos A part position.\n     * @return A data holder.\n     */\n    public static Triple<IPartContainer, PartTypeBase, PartTarget> getContainerPartConstructionData(PartPos pos) {\n        IPartContainer partContainer = PartHelpers.getPartContainer(pos.getPos(), pos.getSide()).orElse(null);\n        if(partContainer == null) {\n            IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"The tile at %s is not a valid part container.\", pos));\n            return null;\n        }\n        IPartType partType = partContainer.getPart(pos.getSide());\n        if(partType == null || !(partType instanceof PartTypeBase)) {\n            IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"The part container at %s side %s does not \" +\n                    \"have a valid part.\", pos, pos.getSide()));\n            return null;\n        }\n        PartTarget target = partType.getTarget(pos, partContainer.getPartState(pos.getSide()));\n        return Triple.of(partContainer, (PartTypeBase) partType, target);\n    }\n\n    /**\n     * Read a part target from a packet buffer.\n     * @param packetBuffer A packet buffer.\n     * @return A part target.\n     */\n    public static PartTarget readPartTarget(RegistryFriendlyByteBuf packetBuffer) {\n        return PartTarget.fromCenter(PacketCodecs.read(packetBuffer, PartPos.class));\n    }\n\n    /**\n     * Read a part from a packet buffer.\n     * @param packetBuffer A packet buffer.\n     * @return A part.\n     * @param <P> The part type type.\n     * @param <S> The part state type.\n     */\n    public static <P extends IPartType<P, S>, S extends IPartState<P>> P readPart(FriendlyByteBuf packetBuffer) {\n        String name = packetBuffer.readUtf();\n        return (P) Objects.requireNonNull(PartTypeRegistry.getInstance().getPartType(Identifier.parse(name)),\n                String.format(\"Could not find a part by name %s\", name));\n    }\n\n    /**\n     * A part and state holder.\n     * @param <P> The part type type.\n     * @param <S> The part state type.\n     */\n    public static class PartStateHolder<P extends IPartType<P, S>, S extends IPartState<P>> {\n\n        private final IPartType<P, S> part;\n        private final S state;\n\n        public PartStateHolder(IPartType<P, S> part, S state) {\n            this.part = part;\n            this.state = state;\n        }\n\n        public IPartType<P, S> getPart() {\n            return part;\n        }\n\n        public S getState() {\n            return state;\n        }\n\n        public static PartStateHolder<?, ?> of(IPartType part, IPartState partState) {\n            return new PartStateHolder(part, partState);\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            PartStateHolder<?, ?> that = (PartStateHolder<?, ?>) o;\n            return Objects.equals(part, that.part) && Objects.equals(state, that.state);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(part, state);\n        }\n\n        @Override\n        public String toString() {\n            return \"PartStateHolder{\" +\n                    \"part=\" + part +\n                    \", state=\" + state +\n                    '}';\n        }\n\n    }\n\n    /**\n     * A callback for setting part state holders.\n     */\n    public static interface IPartStateHolderCallback {\n\n        public void onSet(PartStateHolder<?, ?> partStateHolder);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/helper/WrenchHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.helper;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.tags.TagKey;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\n\nimport javax.annotation.Nullable;\n\n/**\n * Helper methods related to items.\n * @author rubensworks\n */\npublic final class WrenchHelpers {\n\n    public static final TagKey<Item> TAG_WRENCH = TagKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(\"c\", \"tools/wrench\"));\n\n    /**\n     * Checks if the given player can wrench something.\n     * @param player The player.\n     * @param heldItem The item the player is holding.\n     * @param world The world in which the wrenching is happening.\n     * @param pos The position that is being wrenched.\n     * @param side The side that is being wrenched.\n     * @return If the wrenching can continue with the held item.\n     */\n    public static boolean isWrench(Player player, ItemStack heldItem, Level world, BlockPos pos, @Nullable Direction side) {\n        return heldItem.getItem().builtInRegistryHolder().tags().anyMatch(tag -> tag.equals(WrenchHelpers.TAG_WRENCH));\n    }\n\n    /**\n     * Wrench a given position.\n     * Requires the {@link WrenchHelpers#isWrench(Player, ItemStack, Level, BlockPos, Direction)}\n     * to be passed.\n     * Takes an extra parameter of any type that is forwarded to the wrench action.\n     * @param player The player.\n     * @param heldItem The item the player is holding.\n     * @param world The world in which the wrenching is happening.\n     * @param pos The position that is being wrenched.\n     * @param side The side that is being wrenched.\n     * @param action The actual wrench action.\n     * @param parameter An extra parameter that is forwarded to the action.\n     * @param <P> The type of parameter to pass.\n     */\n    public static <P> void wrench(Player player, ItemStack heldItem, Level world, BlockPos pos, Direction side, IWrenchAction<P> action, P parameter) {\n        if (isWrench(player, heldItem, world, pos, side)) {\n            action.onWrench(player, pos, parameter);\n        }\n    }\n\n    /**\n     * Wrench a given position.\n     * Requires the {@link WrenchHelpers#isWrench(Player, ItemStack, Level, BlockPos, Direction)}\n     * to be passed.\n     * @param player The player.\n     * @param heldItem The item the player is holding.\n     * @param world The world in which the wrenching is happening.\n     * @param pos The position that is being wrenched.\n     * @param side The side that is being wrenched.\n     * @param action The actual wrench action.\n     */\n    public static void wrench(Player player, ItemStack heldItem, Level world, BlockPos pos, Direction side, IWrenchAction<Void> action) {\n        wrench(player, heldItem, world, pos, side, action, null);\n    }\n\n    /**\n     * An action that can serve as wrenching.\n     * @param <P> The type of parameter that is passed.\n     */\n    public static interface IWrenchAction<P> {\n\n        /**\n         * Called for the actual wrenching action.\n         * @param player The player.\n         * @param pos The position that is being wrenched.\n         * @param parameter An extra parameter that is used to call this action.\n         */\n        public void onWrench(Player player, BlockPos pos, P parameter);\n\n    }\n\n    /**\n     * An action that can serve as wrenching.\n     */\n    public static abstract class SimpleWrenchAction implements IWrenchAction<Void> {\n\n        /**\n         * Called for the actual wrenching action.\n         * @param player The player.\n         * @param pos The position that is being wrenched.\n         * @param parameter An extra parameter that is used to call this action.\n         */\n        public void onWrench(Player player, BlockPos pos, Void parameter) {\n            onWrench(player, pos);\n        }\n\n        /**\n         * Called for the actual wrenching action.\n         * @param player The player.\n         * @param pos The position that is being wrenched.\n         */\n        public abstract void onWrench(Player player, BlockPos pos);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/ingredient/ExtendedIngredientsList.java",
    "content": "package org.cyclops.integrateddynamics.core.ingredient;\n\nimport com.google.common.collect.Sets;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * A lazy extension for ingredients for a list of instances.\n * @param <T> The instance type.\n * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n * @author rubensworks\n */\npublic class ExtendedIngredientsList<T, M> extends WrappedIngredients {\n\n    private final IngredientComponent<T, M> component;\n    private final List<T> instances;\n\n    public ExtendedIngredientsList(IMixedIngredients base, IngredientComponent<T, M> component, List<T> instances) {\n        super(base);\n        this.component = component;\n        this.instances = instances;\n    }\n\n    protected boolean forComponent(IngredientComponent<?, ?> component) {\n        return component == this.component;\n    }\n\n    @Override\n    public Set<IngredientComponent<?, ?>> getComponents() {\n        Set<IngredientComponent<?, ?>> components = Sets.newIdentityHashSet();\n        components.addAll(super.getComponents());\n        components.add(component);\n        return components;\n    }\n\n    @Override\n    public <T2> List<T2> getInstances(IngredientComponent<T2, ?> ingredientComponent) {\n        List<T2> superList = super.getInstances(ingredientComponent);\n        return forComponent(ingredientComponent) ? (List<T2>) this.instances : superList;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/ingredient/ExtendedIngredientsSingle.java",
    "content": "package org.cyclops.integrateddynamics.core.ingredient;\n\nimport com.google.common.collect.Sets;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\n\nimport java.util.AbstractList;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * A lazy extension for ingredients for a single instance.\n * @param <T> The instance type.\n * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n * @author rubensworks\n */\npublic class ExtendedIngredientsSingle<T, M> extends WrappedIngredients {\n\n    private final int targetIndex;\n\n    private final IngredientComponent<T, M> component;\n    private final T instance;\n\n    public ExtendedIngredientsSingle(IMixedIngredients base, int targetIndex,\n                                     IngredientComponent<T, M> component, T instance) {\n        super(base);\n        this.targetIndex = targetIndex;\n        this.component = component;\n        this.instance = instance;\n    }\n\n    protected boolean forComponent(IngredientComponent<?, ?> component) {\n        return component == this.component;\n    }\n\n    @Override\n    public Set<IngredientComponent<?, ?>> getComponents() {\n        Set<IngredientComponent<?, ?>> components = Sets.newIdentityHashSet();\n        components.addAll(super.getComponents());\n        components.add(component);\n        return components;\n    }\n\n    @Override\n    public <T2> List<T2> getInstances(IngredientComponent<T2, ?> ingredientComponent) {\n        List<T2> superList = super.getInstances(ingredientComponent);\n        return forComponent(ingredientComponent) ? new AbstractList<T2>() {\n            @Override\n            public T2 get(int index) {\n                if (index == targetIndex) {\n                    return (T2) instance;\n                } else if (index < targetIndex && index >= superList.size()) {\n                    return (T2) component.getMatcher().getEmptyInstance();\n                }\n                return superList.get(index);\n            }\n\n            @Override\n            public int size() {\n                int superSize = superList.size();\n                if (targetIndex >= superSize) {\n                    return targetIndex + 1;\n                }\n                return superSize;\n            }\n        } : superList;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/ingredient/IngredientComponentHandlerRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.ingredient;\n\nimport com.google.common.collect.Maps;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandlerRegistry;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * @author rubensworks\n */\npublic class IngredientComponentHandlerRegistry implements IIngredientComponentHandlerRegistry {\n\n    private static IngredientComponentHandlerRegistry INSTANCE = new IngredientComponentHandlerRegistry();\n\n    private final Map<IngredientComponent<?, ?>, IIngredientComponentHandler> componentTypes = Maps.newIdentityHashMap();\n\n    private IngredientComponentHandlerRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static IngredientComponentHandlerRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <VT extends IValueType<V>, V extends IValue,\n            T, M, H extends IIngredientComponentHandler<VT, V, T, M>> H register(H handler) {\n        this.componentTypes.put(Objects.requireNonNull(handler.getComponent(), \"The recipe component of \"\n                + handler + \" was null, it is probably not initialized yet!\"), handler);\n        return handler;\n    }\n\n    @Nullable\n    @Override\n    public <VT extends IValueType<V>, V extends IValue, T, M>\n    IIngredientComponentHandler<VT, V, T, M> getComponentHandler(IngredientComponent<T, M> component) {\n        return this.componentTypes.get(component);\n    }\n\n    @Override\n    public Set<IngredientComponent<?, ?>> getComponents() {\n        return this.componentTypes.keySet();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/ingredient/IngredientComponentHandlers.java",
    "content": "package org.cyclops.integrateddynamics.core.ingredient;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.item.ItemStack;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.registries.RegisterEvent;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandlerRegistry;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeFluidStack;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeLong;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport javax.annotation.Nullable;\n\n/**\n * Value handlers for ingredient components.\n * @author rubensworks\n */\npublic class IngredientComponentHandlers {\n\n    public static final IIngredientComponentHandlerRegistry REGISTRY = constructRegistry();\n\n    private static IIngredientComponentHandlerRegistry constructRegistry() {\n        // This also allows this registry to be used outside of a minecraft environment.\n        if(IModHelpers.get().getMinecraftHelpers().isModdedEnvironment()) {\n            return IntegratedDynamics._instance.getRegistryManager().getRegistry(IIngredientComponentHandlerRegistry.class);\n        } else {\n            return IngredientComponentHandlerRegistry.getInstance();\n        }\n    }\n\n    public static void load() {\n\n    }\n\n    public static void onIngredientComponentsPopulated(RegisterEvent event) {\n        if (event.getRegistryKey().equals(IngredientComponent.REGISTRY.key())) {\n            // Components are still loading here, so grab them by name\n            IngredientComponent componentItem = IngredientComponent.REGISTRY.getValue(\n                    Identifier.parse(\"minecraft:itemstack\"));\n            IngredientComponent componentFluid = IngredientComponent.REGISTRY.getValue(\n                    Identifier.parse(\"minecraft:fluidstack\"));\n            IngredientComponent componentEnergy = IngredientComponent.REGISTRY.getValue(\n                    Identifier.parse(\"minecraft:energy\"));\n\n            REGISTRY.register(new IIngredientComponentHandler<ValueObjectTypeItemStack,\n                    ValueObjectTypeItemStack.ValueItemStack, ItemStack, Integer>() {\n                @Override\n                public ValueObjectTypeItemStack getValueType() {\n                    return ValueTypes.OBJECT_ITEMSTACK;\n                }\n\n                @Override\n                public IngredientComponent<ItemStack, Integer> getComponent() {\n                    return componentItem;\n                }\n\n                @Override\n                public ValueObjectTypeItemStack.ValueItemStack toValue(ItemStack instance) {\n                    return ValueObjectTypeItemStack.ValueItemStack.of(instance);\n                }\n\n                @Override\n                public ItemStack toInstance(ValueObjectTypeItemStack.ValueItemStack value) {\n                    return value.getRawValue();\n                }\n            });\n            REGISTRY.register(new IIngredientComponentHandler<ValueObjectTypeFluidStack,\n                    ValueObjectTypeFluidStack.ValueFluidStack, FluidStack, Integer>() {\n\n                @Override\n                public ValueObjectTypeFluidStack getValueType() {\n                    return ValueTypes.OBJECT_FLUIDSTACK;\n                }\n\n                @Override\n                public IngredientComponent<FluidStack, Integer> getComponent() {\n                    return componentFluid;\n                }\n\n                @Override\n                public ValueObjectTypeFluidStack.ValueFluidStack toValue(@Nullable FluidStack instance) {\n                    return ValueObjectTypeFluidStack.ValueFluidStack.of(instance);\n                }\n\n                @Override\n                @Nullable\n                public FluidStack toInstance(ValueObjectTypeFluidStack.ValueFluidStack value) {\n                    return value.getRawValue();\n                }\n            });\n            REGISTRY.register(new IIngredientComponentHandler<ValueTypeLong, ValueTypeLong.ValueLong, Long, Boolean>() {\n\n                @Override\n                public ValueTypeLong getValueType() {\n                    return ValueTypes.LONG;\n                }\n\n                @Override\n                public IngredientComponent<Long, Boolean> getComponent() {\n                    return componentEnergy;\n                }\n\n                @Override\n                public ValueTypeLong.ValueLong toValue(@Nullable Long instance) {\n                    return ValueTypeLong.ValueLong.of(instance);\n                }\n\n                @Nullable\n                @Override\n                public Long toInstance(ValueTypeLong.ValueLong value) {\n                    return value.getRawValue();\n                }\n\n                @Override\n                public Component toCompactString(ValueTypeLong.ValueLong ingredientValue) {\n                    return getValueType().toCompactString(ingredientValue)\n                            .append(\" \")\n                            .append(Component.translatable(L10NValues.GENERAL_ENERGY_UNIT));\n                }\n            });\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/ingredient/ItemMatchProperties.java",
    "content": "package org.cyclops.integrateddynamics.core.ingredient;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesItemStackTag;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;\nimport org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * @author rubensworks\n */\npublic class ItemMatchProperties {\n\n    static {\n        PacketCodecs.addCodedAction(ItemMatchProperties.class, new PacketCodec.ICodecAction() {\n            @Override\n            public void encode(Object o, RegistryFriendlyByteBuf packetBuffer) {\n                ItemMatchProperties props = ((ItemMatchProperties) o);\n                PacketCodecs.getAction(ItemStack.class).encode(props.itemStack, packetBuffer);\n                packetBuffer.writeBoolean(props.nbt);\n                packetBuffer.writeUtf(props.itemTag != null ? props.itemTag : \"\");\n                packetBuffer.writeInt(props.tagQuantity);\n                packetBuffer.writeBoolean(props.reusable);\n            }\n\n            @Override\n            public Object decode(RegistryFriendlyByteBuf packetBuffer) {\n                ItemStack itemStack = (ItemStack) PacketCodecs.getAction(ItemStack.class).decode(packetBuffer);\n                boolean nbt = packetBuffer.readBoolean();\n                String itemTag = packetBuffer.readUtf(32767);\n                int tagQuantity = packetBuffer.readInt();\n                boolean reusable = packetBuffer.readBoolean();\n                return new ItemMatchProperties(itemStack, nbt, itemTag.isEmpty() ? null : itemTag, tagQuantity, reusable);\n            }\n        });\n    }\n\n    private final ItemStack itemStack;\n    private boolean nbt;\n    @Nullable\n    private String itemTag;\n    private int tagQuantity;\n    private boolean reusable;\n\n    public ItemMatchProperties(ItemStack itemStack) {\n        this(itemStack, false, null, 1, false);\n    }\n\n    public ItemMatchProperties(ItemStack itemStack, boolean nbt, @Nullable String itemTag, int tagQuantity) {\n        this(itemStack, nbt, itemTag, tagQuantity, false);\n    }\n\n    public ItemMatchProperties(ItemStack itemStack, boolean nbt, @Nullable String itemTag, int tagQuantity, boolean reusable) {\n        this.itemStack = itemStack;\n        this.nbt = nbt;\n        this.itemTag = itemTag;\n        this.tagQuantity = tagQuantity;\n        this.reusable = reusable;\n    }\n\n    public ItemStack getItemStack() {\n        return itemStack;\n    }\n\n    public boolean isNbt() {\n        return nbt;\n    }\n\n    public void setNbt(boolean nbt) {\n        this.nbt = nbt;\n    }\n\n    @Nullable\n    public String getItemTag() {\n        return itemTag;\n    }\n\n    public void setItemTag(@Nullable String itemTag) {\n        this.itemTag = itemTag;\n    }\n\n    public int getTagQuantity() {\n        return tagQuantity;\n    }\n\n    public void setTagQuantity(int tagQuantity) {\n        this.tagQuantity = tagQuantity;\n    }\n\n    public boolean isReusable() {\n        return reusable;\n    }\n\n    public void setReusable(boolean reusable) {\n        this.reusable = reusable;\n    }\n\n    public boolean isValid() {\n        return getItemTag() != null || !getItemStack().isEmpty();\n    }\n\n    public IPrototypedIngredientAlternatives<ItemStack, Integer> createPrototypedIngredient() {\n        int flags = isNbt() ? ItemMatch.ITEM | ItemMatch.DATA : ItemMatch.ITEM;\n        if (getItemTag() == null) {\n            return new PrototypedIngredientAlternativesList<>(\n                    Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, itemStack, flags)));\n        } else {\n            return new PrototypedIngredientAlternativesItemStackTag(Collections.singletonList(getItemTag()),\n                    flags, getTagQuantity());\n        }\n    }\n\n    public static ItemMatchProperties fromPrototypedIngredient(IPrototypedIngredientAlternatives<ItemStack, Integer> prototypedIngredient, boolean reusable) {\n        ItemMatchProperties props = new ItemMatchProperties(ItemStack.EMPTY);\n        if (prototypedIngredient instanceof PrototypedIngredientAlternativesItemStackTag prototypedTag) {\n            prototypedTag.getKeys().stream().findFirst().ifPresent(props::setItemTag);\n            props.setTagQuantity((int) prototypedTag.getQuantity());\n        } else if (prototypedIngredient instanceof PrototypedIngredientAlternativesList<ItemStack, Integer> prototypedList) {\n            Collection<IPrototypedIngredient<ItemStack, Integer>> alternatives = prototypedList.getAlternatives();\n            IPrototypedIngredient<ItemStack, Integer> prototype = alternatives.stream().findFirst().orElse(null);\n            if (prototype != null) {\n                props = new ItemMatchProperties(prototype.getPrototype());\n                props.setNbt(IngredientComponent.ITEMSTACK.getMatcher().hasCondition(prototype.getCondition(), ItemMatch.DATA));\n            }\n        }\n        props.setReusable(reusable);\n        return props;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/ingredient/WrappedIngredients.java",
    "content": "package org.cyclops.integrateddynamics.core.ingredient;\n\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredientsAdapter;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * A wrapper around ingredients.\n * @author rubensworks\n */\npublic class WrappedIngredients extends MixedIngredientsAdapter {\n\n    private final IMixedIngredients ingredients;\n\n    public WrappedIngredients(IMixedIngredients ingredients) {\n        this.ingredients = ingredients;\n    }\n\n    @Override\n    public Set<IngredientComponent<?, ?>> getComponents() {\n        return ingredients.getComponents();\n    }\n\n    @Override\n    public <T> List<T> getInstances(IngredientComponent<T, ?> ingredientComponent) {\n        return ingredients.getInstances(ingredientComponent);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerActiveVariableBase.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.MenuType;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Base container for part entities that can hold variables.\n * @author rubensworks\n */\npublic class ContainerActiveVariableBase<T extends BlockEntityActiveVariableBase> extends InventoryContainer {\n\n    private final Optional<T> tileSupplier;\n    private final int readValueId;\n    private final int readColorId;\n    private final int readErrorsId;\n\n    public ContainerActiveVariableBase(@Nullable MenuType<?> type, int id, Inventory playerInventory,\n                                       Container inventory, Optional<T> tileSupplier) {\n        super(type, id, playerInventory, inventory);\n        this.tileSupplier = tileSupplier;\n        this.readValueId = getNextValueId();\n        this.readColorId = getNextValueId();\n        this.readErrorsId = getNextValueId();\n    }\n\n    @Override\n    public void broadcastChanges() {\n        super.broadcastChanges();\n\n        tileSupplier.ifPresent(tile -> {\n            NetworkHelpers.getPartNetwork(tile.getNetwork())\n                    .ifPresent(partNetwork -> {\n                        IVariable variable = tile.getVariable(partNetwork);\n                        Pair<MutableComponent, Integer> readValue = ValueHelpers.getSafeReadableValue(variable);\n                        ValueNotifierHelpers.setValue(this, readValueId, readValue.getLeft());\n                        ValueNotifierHelpers.setValue(this, readColorId, readValue.getRight());\n                        ValueNotifierHelpers.setValue(this, readErrorsId, tile.getEvaluator().getErrors());\n                    });\n        });\n    }\n\n    public Optional<T> getTileSupplier() {\n        return tileSupplier;\n    }\n\n    public Component getReadValue() {\n        return ValueNotifierHelpers.getValueTextComponent(this, readValueId);\n    }\n\n    public int getReadValueColor() {\n        return ValueNotifierHelpers.getValueInt(this, readColorId);\n    }\n\n    public List<Component> getReadErrors() {\n        return ValueNotifierHelpers.getValueTextComponentList(this, readErrorsId);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerAspectSettings.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport com.google.common.collect.BiMap;\nimport com.google.common.collect.HashBiMap;\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.slf4j.Logger;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Container for aspect settings.\n * @author rubensworks\n */\npublic class ContainerAspectSettings extends InventoryContainer {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    public static final String BUTTON_EXIT = \"button_exit\";\n    public static final int BUTTON_SETTINGS = 1;\n    private static final int PAGE_SIZE = 3;\n\n    private final Optional<PartTarget> target;\n    private final Optional<IPartContainer> partContainer;\n    private final Optional<IPartType> partType;\n    private final Level world;\n    private final IAspect<?, ?> aspect;\n\n    private final BiMap<Integer, IAspectPropertyTypeInstance> propertyIds = HashBiMap.create();\n\n    public ContainerAspectSettings(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, new SimpleContainer(0),\n                Optional.empty(), Optional.empty(), Optional.empty(), readAspect(packetBuffer));\n    }\n\n    protected static IAspect<?, ?> readAspect(FriendlyByteBuf packetBuffer) {\n        String name = packetBuffer.readUtf();\n        return Objects.requireNonNull(AspectRegistry.getInstance().getAspect(Identifier.parse(name)),\n                String.format(\"Could not find an aspect by name %s\", name));\n    }\n\n    public ContainerAspectSettings(int id, Inventory playerInventory, Container inventory,\n                                   Optional<PartTarget> target, Optional<IPartContainer> partContainer,\n                                   Optional<IPartType> partType, IAspect<?, ?> aspect) {\n        super(RegistryEntries.CONTAINER_ASPECT_SETTINGS.get(), id, playerInventory, inventory);\n        this.target = target;\n        this.partType = partType;\n        this.partContainer = partContainer;\n        this.world = player.level();\n        this.aspect = aspect;\n\n        addPlayerInventory(player.getInventory(), 8, 131);\n\n        for(IAspectPropertyTypeInstance property : aspect.getPropertyTypes()) {\n            propertyIds.put(getNextValueId(), property);\n        }\n\n        putButtonAction(ContainerAspectSettings.BUTTON_EXIT, (s, containerExtended) -> {\n            if (!world.isClientSide()) {\n                PartHelpers.openContainerPart((ServerPlayer) playerInventory.player, getTarget().get().getCenter(), getPartType().get());\n            }\n        });\n    }\n\n    public BiMap<Integer, IAspectPropertyTypeInstance> getPropertyIds() {\n        return propertyIds;\n    }\n\n    public Optional<IPartType> getPartType() {\n        return partType;\n    }\n\n    public IAspect getAspect() {\n        return aspect;\n    }\n\n    public Optional<PartTarget> getTarget() {\n        return target;\n    }\n\n    @Override\n    protected void initializeValues() {\n        super.initializeValues();\n        IAspectProperties properties = aspect.getProperties(getPartType().get(), getTarget().get(), getPartState().get());\n        for(IAspectPropertyTypeInstance property : aspect.getPropertyTypes()) {\n            setValue(ValueDeseralizationContext.ofAllEnabled(), property, properties.getValue(property));\n        }\n    }\n\n    public void setValue(ValueDeseralizationContext valueDeseralizationContext, IAspectPropertyTypeInstance property, IValue value) {\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) {\n            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n            ValueHelpers.serializeRaw(valueOutput, value);\n            ValueNotifierHelpers.setValue(this, propertyIds.inverse().get(property), valueOutput.buildResult());\n        }\n    }\n\n    public Optional<IPartState> getPartState() {\n        return partContainer.map(p -> p.getPartState(getTarget().get().getCenter().getSide()));\n    }\n\n    @Override\n    public boolean stillValid(Player player) {\n        return true;\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return 0;\n    }\n\n    public <T extends IValueType<V>, V extends IValue> V getPropertyValue(ValueDeseralizationContext valueDeseralizationContext, IAspectPropertyTypeInstance<T, V> property) {\n        if(propertyIds.containsValue(property)) {\n            CompoundTag value = (CompoundTag) ValueNotifierHelpers.getValueNbt(this, propertyIds.inverse().get(property));\n            if(value != null) {\n                try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) {\n                    ValueInput input = TagValueInput.create(\n                            scopedCollector,\n                            valueDeseralizationContext.holderLookupProvider(),\n                            value\n                    );\n                    return ValueHelpers.deserializeRaw(input, property.getType());\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        super.onUpdate(valueId, value);\n        if(!world.isClientSide()) {\n            IAspectPropertyTypeInstance property = propertyIds.get(valueId);\n            if (property != null) {\n                IPartType partType = getPartType().get();\n                PartTarget target = getTarget().get();\n                IPartState partState = getPartState().get();\n\n                IAspectProperties aspectProperties = aspect.getProperties(partType, target, partState);\n                aspectProperties = aspectProperties.clone();\n                IValue trueValue;\n                try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) {\n                    ValueInput input = TagValueInput.create(\n                            scopedCollector,\n                            player.level().registryAccess(),\n                            (CompoundTag) value.get(ValueNotifierHelpers.KEY)\n                    );\n                    trueValue = ValueHelpers.deserializeRaw(input, property.getType());\n                }\n                aspectProperties.setValue(property, trueValue);\n                aspect.setProperties(partType, target, partState, aspectProperties);\n\n                // Changing the properties might cause some erroring variables to become valid again, so trigger an update.\n                NetworkHelpers.getNetwork(target.getCenter())\n                        .ifPresent(network -> network.getEventBus().post(new VariableContentsUpdatedEvent(network)));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerAspectSettingsConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerAspectSettings}.\n * @author rubensworks\n */\npublic class ContainerAspectSettingsConfig extends GuiConfigCommon<ContainerAspectSettings, IntegratedDynamics> {\n\n    public ContainerAspectSettingsConfig() {\n        super(IntegratedDynamics._instance,\n                \"aspect_settings\",\n                eConfig -> new ContainerTypeData<>(ContainerAspectSettings::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerAspectSettings> getScreenFactoryProvider() {\n        return new ContainerAspectSettingsConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerAspectSettingsConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenAspectSettings;\n\n/**\n * @author rubensworks\n */\npublic class ContainerAspectSettingsConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerAspectSettings> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerAspectSettings>> MenuScreens.ScreenConstructor<ContainerAspectSettings, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenAspectSettings::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerMechanicalMachine.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * A base container for {@link BlockEntityMechanicalMachine}.\n * @author rubensworks\n */\npublic class ContainerMechanicalMachine<T extends BlockEntityMechanicalMachine<?, ?>> extends InventoryContainer {\n\n    private final Optional<T> tileSupplier;\n    private final Supplier<Integer> variableMaxProgress;\n    private final Supplier<Integer> variableProgress;\n    private final Supplier<Integer> variableMaxEnergy;\n    private final Supplier<Integer> variableEnergy;\n\n    public ContainerMechanicalMachine(@Nullable MenuType<?> type, int id, Inventory playerInventory,\n                                      Container inventory, Optional<T> tileSupplier) {\n        super(type, id, playerInventory, inventory);\n        this.tileSupplier = tileSupplier;\n        this.variableMaxProgress = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getMaxProgress());\n        this.variableProgress = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getProgress());\n        this.variableMaxEnergy = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getEnergyHandler().getCapacityAsInt());\n        this.variableEnergy = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getEnergyHandler().getAmountAsInt());\n    }\n\n    public Optional<T> getTileSupplier() {\n        return tileSupplier;\n    }\n\n    public int getMaxProgress() {\n        return variableMaxProgress.get();\n    }\n\n    public int getProgress() {\n        return variableProgress.get();\n    }\n\n    public int getMaxEnergy() {\n        return variableMaxEnergy.get();\n    }\n\n    public int getEnergy() {\n        return variableEnergy.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerMultipart.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Container for parts.\n * @author rubensworks\n */\npublic abstract class ContainerMultipart<P extends IPartType<P, S>, S extends IPartState<P>>\n        extends InventoryContainer implements IDirtyMarkListener {\n\n    public static final String BUTTON_SETTINGS = \"button_settings\";\n    public static final String BUTTON_OFFSETS = \"button_offsets\";\n\n    private static final int PAGE_SIZE = 3;\n\n    private final Optional<PartTarget> target;\n    private final Optional<IPartContainer> partContainer;\n    private final P partType;\n    private final Level world;\n\n    public ContainerMultipart(@Nullable MenuType<?> type, int id, Inventory playerInventory, Container inventory,\n                              Optional<PartTarget> target, Optional<IPartContainer> partContainer, P partType) {\n        super(type, id, playerInventory, inventory);\n        this.target = target;\n        this.partType = partType;\n        this.partContainer = partContainer;\n        this.world = player.level();\n\n        putButtonAction(ContainerMultipart.BUTTON_SETTINGS, (s, containerExtended) -> {\n            if(!world.isClientSide()) {\n                PartHelpers.openContainerPart((ServerPlayer) player, target.get().getCenter(), partType);\n            }\n        });\n        putButtonAction(ContainerMultipart.BUTTON_OFFSETS, (s, containerExtended) -> {\n            if(!world.isClientSide()) {\n                PartHelpers.openContainerPartOffsets((ServerPlayer) player, target.get().getCenter(), partType);\n            }\n        });\n    }\n\n    public Level getLevel() {\n        return world;\n    }\n\n    public P getPartType() {\n        return partType;\n    }\n\n    public Optional<PartTarget> getTarget() {\n        return target;\n    }\n\n    public Optional<S> getPartState() {\n        return partContainer.map(p -> (S) p.getPartState(getTarget().get().getCenter().getSide()));\n    }\n\n    public Optional<IPartContainer> getPartContainer() {\n        return partContainer;\n    }\n\n    @Override\n    public boolean stillValid(Player playerIn) {\n        return PartHelpers.canInteractWith(getTarget().get(), player, this.partContainer.get());\n    }\n\n    @Override\n    public void onDirty() {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerMultipartAspects.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.container.ScrollingInventoryContainer;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.item.IAspectVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.item.AspectVariableFacade;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * Container for parts.\n *\n * @author rubensworks\n */\npublic abstract class ContainerMultipartAspects<P extends IPartType<P, S>, S extends IPartState<P>, A extends IAspect<?, ?>>\n        extends ScrollingInventoryContainer<A> implements IDirtyMarkListener {\n\n    public static String BUTTON_SETTINGS = \"button_settings\";\n    public static String BUTTON_OFFSETS = \"button_offsets\";\n\n    private static final int PAGE_SIZE = 3;\n\n    private final PartTarget target;\n    private final IPartContainer partContainer;\n    private final P partType;\n    private final Level world;\n    private final Map<IAspect, String> aspectPropertyButtons = Maps.newHashMap();\n\n    protected final Container inputSlots;\n\n    public ContainerMultipartAspects(@Nullable MenuType<?> type, int id, Inventory playerInventory, Container inventory,\n                                     PartTarget target, Optional<IPartContainer> partContainer, P partType,\n                                     List<A> items) {\n        super(type, id, playerInventory, inventory, items, (item, pattern) -> {\n            // We could cache this if this would prove to be a bottleneck.\n            // But we have a small amount of aspects, so this shouldn't be a problem.\n            return pattern.matcher(IModHelpers.get().getL10NHelpers().localize(item.getTranslationKey()).toLowerCase(Locale.ENGLISH)).matches();\n        });\n        this.target = target;\n        this.partContainer = partContainer.orElseGet(() -> PartHelpers.getPartContainerChecked(target.getCenter()));\n        this.partType = partType;\n        this.world = player.level();\n\n        this.inputSlots = constructInputSlotsInventory();\n\n        putButtonAction(ContainerMultipartAspects.BUTTON_SETTINGS, (s, containerExtended) -> {\n            if (!world.isClientSide()) {\n                PartHelpers.openContainerPartSettings((ServerPlayer) player, target.getCenter(), partType);\n            }\n        });\n        putButtonAction(ContainerMultipartAspects.BUTTON_OFFSETS, (s, containerExtended) -> {\n            if (!world.isClientSide()) {\n                PartHelpers.openContainerPartOffsets((ServerPlayer) player, target.getCenter(), partType);\n            }\n        });\n\n        for (final IAspect aspect : getUnfilteredItems()) {\n            if (aspect.hasProperties()) {\n                String buttonId = \"button_aspect_\" + aspect.getUniqueName();\n                aspectPropertyButtons.put(aspect, buttonId);\n                putButtonAction(buttonId, (s, containerExtended) -> {\n                    if (!world.isClientSide()) {\n                        PartHelpers.openContainerAspectSettings((ServerPlayer) player, target.getCenter(), aspect);\n                    }\n                });\n            }\n        }\n    }\n\n    public P getPartType() {\n        return partType;\n    }\n\n    public PartTarget getTarget() {\n        return target;\n    }\n\n    public IPartContainer getPartContainer() {\n        return partContainer;\n    }\n\n    public S getPartState() {\n        return (S) partContainer.getPartState(getTarget().getCenter().getSide());\n    }\n\n    public Map<IAspect, String> getAspectPropertyButtons() {\n        return Collections.unmodifiableMap(this.aspectPropertyButtons);\n    }\n\n    public abstract int getAspectBoxHeight();\n\n    protected Container constructInputSlotsInventory() {\n        SimpleInventory inventory = new SimpleInventory(getUnfilteredItemCount(), 1);\n        inventory.addDirtyMarkListener(this);\n        return inventory;\n    }\n\n    @Override\n    public void removed(Player player) {\n        super.removed(player);\n        if (inputSlots instanceof SimpleInventory) {\n            ((SimpleInventory) inputSlots).removeDirtyMarkListener(this);\n        }\n    }\n\n    protected void disableSlot(int slotIndex) {\n        Slot slot = getSlot(slotIndex);\n        // Yes I know this is ugly.\n        // If you are reading this and know a better way, please tell me.\n        setSlotPosX(slot, Integer.MIN_VALUE);\n        setSlotPosY(slot, Integer.MIN_VALUE);\n    }\n\n    protected abstract void enableSlot(int slotIndex, int row);\n\n    @Override\n    public int getPageSize() {\n        return PAGE_SIZE;\n    }\n\n    @Override\n    public void onScroll(int firstRow) {\n        for (int i = 0; i < getUnfilteredItemCount(); i++) {\n            disableSlot(i);\n        }\n        super.onScroll(firstRow);\n    }\n\n    @Override\n    protected void enableElementAt(int row, int elementIndex, A element) {\n        super.enableElementAt(row, elementIndex, element);\n        enableSlot(elementIndex, row);\n    }\n\n    @Override\n    public boolean stillValid(Player player) {\n        return PartHelpers.canInteractWith(getTarget(), player, this.partContainer);\n    }\n\n    public ItemStack writeAspectInfo(boolean generateId, ItemStack itemStack, Level level, final IAspect aspect) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        return registry.writeVariableFacadeItem(generateId, itemStack, Aspects.REGISTRY, new IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IAspectVariableFacade>() {\n            @Override\n            public IAspectVariableFacade create(boolean generateId) {\n                return new AspectVariableFacade(generateId, getPartState().getId(), aspect);\n            }\n\n            @Override\n            public IAspectVariableFacade create(int id) {\n                return new AspectVariableFacade(id, getPartState().getId(), aspect);\n            }\n        }, level, null, null);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerPartOffset.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Container for part offsets.\n * @author rubensworks\n */\npublic class ContainerPartOffset extends InventoryContainer {\n\n    public static final String BUTTON_SAVE = \"button_save\";\n\n    private final PartTarget target;\n    private final Optional<IPartContainer> partContainer;\n    private final IPartType partType;\n    private final Level world;\n\n    private final int lastXValueId;\n    private final int lastYValueId;\n    private final int lastZValueId;\n    private final List<Integer> offsetVariableSlotErrorIds;\n    private final List<Integer> offsetVariableSlotFilled;\n    private final int maxOffsetId;\n\n    private final SimpleInventory offsetVariablesInventory;\n    private boolean dirtyInv = false;\n\n    public ContainerPartOffset(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, (RegistryFriendlyByteBuf) packetBuffer);\n    }\n\n    public ContainerPartOffset(int id, Inventory playerInventory, RegistryFriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, new SimpleContainer(0),\n                PartHelpers.readPartTarget(packetBuffer), Optional.empty(), PartHelpers.readPart(packetBuffer));\n    }\n\n    public ContainerPartOffset(int id, Inventory playerInventory, Container inventory,\n                               PartTarget target, Optional<IPartContainer> partContainer, IPartType partType) {\n      this(RegistryEntries.CONTAINER_PART_OFFSET.get(), id, playerInventory, inventory, target, partContainer, partType);\n    }\n\n    public ContainerPartOffset(@Nullable MenuType<?> type, int id, Inventory playerInventory, Container inventory,\n                               PartTarget target, Optional<IPartContainer> partContainer, IPartType partType) {\n        super(type, id, playerInventory, inventory);\n        this.target = target;\n        this.partContainer = partContainer;\n        this.partType = partType;\n        this.world = player.level();\n\n        lastXValueId = getNextValueId();\n        lastYValueId = getNextValueId();\n        lastZValueId = getNextValueId();\n        this.offsetVariableSlotErrorIds = Lists.newArrayList();\n        this.offsetVariableSlotFilled = Lists.newArrayList();\n        for (int i = 0; i < 3; i++) {\n            this.offsetVariableSlotErrorIds.add(getNextValueId());\n            this.offsetVariableSlotFilled.add(getNextValueId());\n        }\n        this.maxOffsetId = getNextValueId();\n\n        putButtonAction(ContainerPartOffset.BUTTON_SAVE, (s, containerExtended) -> {\n            if(!world.isClientSide()) {\n                PartHelpers.openContainerPart((ServerPlayer) player, target.getCenter(), getPartType());\n            }\n        });\n\n        offsetVariablesInventory = new SimpleInventory(3, 1);\n        offsetVariablesInventory.addDirtyMarkListener(() -> dirtyInv = true);\n        if (!player.level().isClientSide()) {\n            getPartState().loadInventoryNamed(\"offsetVariablesInventory\", offsetVariablesInventory);\n        }\n        addSlot(new SlotVariable(offsetVariablesInventory, 0, 45, 51));\n        addSlot(new SlotVariable(offsetVariablesInventory, 1, 99, 51));\n        addSlot(new SlotVariable(offsetVariablesInventory, 2, 153, 51));\n\n        addPlayerInventory(player.getInventory(), 27, getPlayerInventoryOffsetY());\n    }\n\n    public IPartType getPartType() {\n        return partType;\n    }\n\n    public PartTarget getTarget() {\n        return target;\n    }\n\n    public int getLastXValueId() {\n        return lastXValueId;\n    }\n\n    public int getLastYValueId() {\n        return lastYValueId;\n    }\n\n    public int getLastZValueId() {\n        return lastZValueId;\n    }\n\n    public int getMaxOffsetId() {\n        return maxOffsetId;\n    }\n\n    protected int getPlayerInventoryOffsetY() {\n        return 73;\n    }\n\n    @Override\n    protected void initializeValues() {\n        Vec3i offset = getPartType().getTargetOffset(getPartState());\n        ValueNotifierHelpers.setValue(this, lastXValueId, offset.getX());\n        ValueNotifierHelpers.setValue(this, lastYValueId, offset.getY());\n        ValueNotifierHelpers.setValue(this, lastZValueId, offset.getZ());\n        ValueNotifierHelpers.setValue(this, maxOffsetId, getPartState().getMaxOffset());\n    }\n\n    public int getLastXValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastXValueId);\n    }\n\n    public int getLastYValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastYValueId);\n    }\n\n    public int getLastZValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastZValueId);\n    }\n\n    public int getMaxOffset() {\n        return ValueNotifierHelpers.getValueInt(this, maxOffsetId);\n    }\n\n    public IPartState getPartState() {\n        return partContainer.get().getPartState(getTarget().getCenter().getSide());\n    }\n\n    @Override\n    public boolean stillValid(Player player) {\n        return PartHelpers.canInteractWith(getTarget(), player, this.partContainer.get());\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return 0;\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        super.onUpdate(valueId, value);\n        try {\n            if(!world.isClientSide()) {\n                updatePartOffset();\n            }\n        } catch (PartStateException e) {\n            player.closeContainer();\n        }\n    }\n\n    @Override\n    public void broadcastChanges() {\n        super.broadcastChanges();\n\n        if (!player.level().isClientSide()) {\n            IPartState partState = getPartState();\n\n            if (this.dirtyInv) {\n                this.dirtyInv = false;\n                partState.saveInventoryNamed(\"offsetVariablesInventory\", offsetVariablesInventory);\n                getPartType().onOffsetVariablesChanged(getTarget(), partState);\n            }\n\n            for (int i = 0; i < 3; i++) {\n                ValueNotifierHelpers.setValue(this, this.offsetVariableSlotErrorIds.get(i), partState.getOffsetVariableError(i));\n                ValueNotifierHelpers.setValue(this, this.offsetVariableSlotFilled.get(i), !offsetVariablesInventory.getItem(i).isEmpty());\n            }\n\n            Vec3i offset = getPartType().getTargetOffset(getPartState());\n            ValueNotifierHelpers.setValue(this, lastXValueId, offset.getX());\n            ValueNotifierHelpers.setValue(this, lastYValueId, offset.getY());\n            ValueNotifierHelpers.setValue(this, lastZValueId, offset.getZ());\n        }\n    }\n\n    @Nullable\n    public Component getOffsetVariableError(int slot) {\n        return ValueNotifierHelpers.getValueTextComponent(this, this.offsetVariableSlotErrorIds.get(slot));\n    }\n\n    public boolean isOffsetVariableFilled(int slot) {\n        return ValueNotifierHelpers.getValueBoolean(this, this.offsetVariableSlotFilled.get(slot));\n    }\n\n    protected void updatePartOffset() {\n        Vec3i offset = new Vec3i(getLastXValue(), getLastYValue(), getLastZValue());\n        getPartType().setTargetOffset(getPartState(), getTarget().getCenter(), offset);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerPartOffsetConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerPartOffset}.\n * @author rubensworks\n */\npublic class ContainerPartOffsetConfig extends GuiConfigCommon<ContainerPartOffset, IntegratedDynamics> {\n\n    public ContainerPartOffsetConfig() {\n        super(IntegratedDynamics._instance,\n                \"part_offset\",\n                eConfig -> new ContainerTypeData<>(ContainerPartOffset::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerPartOffset> getScreenFactoryProvider() {\n        return new ContainerPartOffsetConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerPartOffsetConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenPartOffset;\n\n/**\n * @author rubensworks\n */\npublic class ContainerPartOffsetConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerPartOffset> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerPartOffset>> MenuScreens.ScreenConstructor<ContainerPartOffset, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(new MenuScreens.ScreenConstructor<ContainerPartOffset, ContainerScreenPartOffset<ContainerPartOffset>>() {\n            @Override\n            public ContainerScreenPartOffset<ContainerPartOffset> create(ContainerPartOffset container, Inventory playerInventory, Component title) {\n                return new ContainerScreenPartOffset<ContainerPartOffset>(container, playerInventory, title);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerPartSettings.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.network.PartNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Container for part settings.\n * @author rubensworks\n */\npublic class ContainerPartSettings extends InventoryContainer {\n\n    public static final String BUTTON_SAVE = \"button_save\";\n    public static final String BUTTON_SETTINGS = \"button_settings\";\n    private static final int PAGE_SIZE = 3;\n\n    private final PartTarget target;\n    private final Optional<IPartContainer> partContainer;\n    private final IPartType partType;\n    private final Level world;\n\n    private final int lastUpdateValueId;\n    private final int lastPriorityValueId;\n    private final int lastChannelValueId;\n    private final int lastSideValueId;\n    private final int lastMinUpdateValueId;\n\n    public ContainerPartSettings(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, (RegistryFriendlyByteBuf) packetBuffer);\n    }\n\n    public ContainerPartSettings(int id, Inventory playerInventory, RegistryFriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, new SimpleContainer(0),\n                PartHelpers.readPartTarget(packetBuffer), Optional.empty(), PartHelpers.readPart(packetBuffer));\n    }\n\n    public ContainerPartSettings(int id, Inventory playerInventory, Container inventory,\n                                 PartTarget target, Optional<IPartContainer> partContainer, IPartType partType) {\n      this(RegistryEntries.CONTAINER_PART_SETTINGS.get(), id, playerInventory, inventory, target, partContainer, partType);\n    }\n\n    public ContainerPartSettings(@Nullable MenuType<?> type, int id, Inventory playerInventory, Container inventory,\n                                 PartTarget target, Optional<IPartContainer> partContainer, IPartType partType) {\n        super(type, id, playerInventory, inventory);\n        this.target = target;\n        this.partContainer = partContainer;\n        this.partType = partType;\n        this.world = player.level();\n\n        addPlayerInventory(player.getInventory(), 27, getPlayerInventoryOffsetY());\n\n        lastUpdateValueId = getNextValueId();\n        lastPriorityValueId = getNextValueId();\n        lastChannelValueId = getNextValueId();\n        lastSideValueId = getNextValueId();\n        lastMinUpdateValueId = getNextValueId();\n\n        putButtonAction(ContainerPartSettings.BUTTON_SAVE, (s, containerExtended) -> {\n            if(!world.isClientSide()) {\n                PartHelpers.openContainerPart((ServerPlayer) player, target.getCenter(), getPartType());\n            }\n        });\n    }\n\n    public IPartType getPartType() {\n        return partType;\n    }\n\n    public PartTarget getTarget() {\n        return target;\n    }\n\n    public int getLastChannelValueId() {\n        return lastChannelValueId;\n    }\n\n    public int getLastPriorityValueId() {\n        return lastPriorityValueId;\n    }\n\n    public int getLastSideValueId() {\n        return lastSideValueId;\n    }\n\n    public int getLastUpdateValueId() {\n        return lastUpdateValueId;\n    }\n\n    public int getLastMinUpdateValueId() {\n        return lastMinUpdateValueId;\n    }\n\n    protected int getPlayerInventoryOffsetY() {\n        return 107;\n    }\n\n    @Override\n    protected void initializeValues() {\n        ValueNotifierHelpers.setValue(this, lastUpdateValueId, getPartType().getUpdateInterval(getPartState()));\n        ValueNotifierHelpers.setValue(this, lastPriorityValueId, getPartType().getPriority(getPartState()));\n        ValueNotifierHelpers.setValue(this, lastChannelValueId, getPartType().getChannel(getPartState()));\n        Direction targetSide = getPartType().getTargetSideOverride(getPartState());\n        ValueNotifierHelpers.setValue(this, lastSideValueId, targetSide == null ? -1 : targetSide.ordinal());\n        ValueNotifierHelpers.setValue(this, lastMinUpdateValueId, getPartType().getMinimumUpdateInterval(getPartState()));\n    }\n\n    public int getLastUpdateValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastUpdateValueId);\n    }\n\n    public int getLastPriorityValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastPriorityValueId);\n    }\n\n    public int getLastChannelValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastChannelValueId);\n    }\n\n    public int getLastSideValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastSideValueId);\n    }\n\n    public int getLastMinUpdateValue() {\n        return ValueNotifierHelpers.getValueInt(this, lastMinUpdateValueId);\n    }\n\n    public IPartState getPartState() {\n        return partContainer.get().getPartState(getTarget().getCenter().getSide());\n    }\n\n    @Override\n    public boolean stillValid(Player player) {\n        return PartHelpers.canInteractWith(getTarget(), player, this.partContainer.get());\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return 0;\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        super.onUpdate(valueId, value);\n        try {\n            if(!world.isClientSide()) {\n                PartTarget target = getTarget();\n                DimPos dimPos = target.getCenter().getPos();\n                INetwork network = NetworkHelpers.getNetworkChecked(dimPos.getLevel(true), dimPos.getBlockPos(), target.getCenter().getSide());\n                updatePartSettings();\n                PartNetworkElement networkElement = new PartNetworkElement<>(getPartType(), target.getCenter());\n                network.setPriorityAndChannel(networkElement, getLastPriorityValue(), getLastChannelValue());\n            }\n        } catch (PartStateException e) {\n            player.closeContainer();\n        }\n    }\n\n    protected void updatePartSettings() {\n        getPartType().setUpdateInterval(getPartState(), getLastUpdateValue());\n        Direction targetSide = getLastSideValue() >= 0 ? Direction.values()[getLastSideValue()] : null;\n        getPartType().setTargetSideOverride(getPartState(), targetSide);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerPartSettingsConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerPartSettings}.\n * @author rubensworks\n */\npublic class ContainerPartSettingsConfig extends GuiConfigCommon<ContainerPartSettings, IntegratedDynamics> {\n\n    public ContainerPartSettingsConfig() {\n        super(IntegratedDynamics._instance,\n                \"part_settings\",\n                eConfig -> new ContainerTypeData<>(ContainerPartSettings::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerPartSettings> getScreenFactoryProvider() {\n        return new ContainerPartSettingsConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/ContainerPartSettingsConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenPartSettings;\n\n/**\n * @author rubensworks\n */\npublic class ContainerPartSettingsConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerPartSettings> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerPartSettings>> MenuScreens.ScreenConstructor<ContainerPartSettings, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(new MenuScreens.ScreenConstructor<ContainerPartSettings, ContainerScreenPartSettings<ContainerPartSettings>>() {\n            @Override\n            public ContainerScreenPartSettings<ContainerPartSettings> create(ContainerPartSettings container, Inventory playerInventory, Component title) {\n                return new ContainerScreenPartSettings<ContainerPartSettings>(container, playerInventory, title);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/inventory/container/slot/SlotVariable.java",
    "content": "package org.cyclops.integrateddynamics.core.inventory.container.slot;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.Container;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.inventory.slot.SlotSingleItem;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Slot for a variable item.\n * @author rubensworks\n */\npublic class SlotVariable extends SlotSingleItem {\n\n    public static Identifier VARIABLE_EMPTY = Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"slot/variable_empty\");\n\n    /**\n     * Make a new instance.\n     *\n     * @param inventory The inventory this slot will be in.\n     * @param index     The index of this slot.\n     * @param x         X coordinate.\n     * @param y         Y coordinate.\n     */\n    public SlotVariable(Container inventory, int index, int x, int y) {\n        super(inventory, index, x, y, RegistryEntries.ITEM_VARIABLE.get());\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            setBackground(SlotVariable.VARIABLE_EMPTY);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/AspectVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.Item;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IAspectVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * Variable facade for variables determined by part aspects.\n * @author rubensworks\n */\npublic class AspectVariableFacade extends VariableFacadeBase implements IAspectVariableFacade {\n\n    private final int partId;\n    private final IAspect aspect;\n\n    public AspectVariableFacade(boolean generateId, int partId, IAspect aspect) {\n        super(generateId);\n        this.partId = partId;\n        this.aspect = aspect;\n    }\n\n    public AspectVariableFacade(int id, int partId, IAspect aspect) {\n        super(id);\n        this.partId = partId;\n        this.aspect = aspect;\n    }\n\n    public int getPartId() {\n        return partId;\n    }\n\n    public IAspect getAspect() {\n        return aspect;\n    }\n\n    @Override\n    public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork) {\n        if(isValid() && getAspect() instanceof IAspectRead && partNetwork.hasPartVariable(getPartId(), (IAspectRead<IValue, ?>) getAspect())) {\n            return partNetwork.getPartVariable(getPartId(), (IAspectRead) getAspect());\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isValid() {\n        return getPartId() >= 0 && getAspect() != null;\n    }\n\n    @Override\n    public void validate(INetwork network, IPartNetwork partNetwork, IValidator validator, IValueType containingValueType) {\n        if (!isValid()) {\n            validator.addError(Component.translatable(L10NValues.VARIABLE_ERROR_INVALIDITEM));\n        } else if (!(getAspect() instanceof IAspectRead\n                && partNetwork.hasPartVariable(getPartId(), (IAspectRead<IValue, ?>) getAspect()))) {\n            validator.addError(Component.translatable(L10NValues.VARIABLE_ERROR_PARTNOTINNETWORK,\n                    Integer.toString(getPartId())));\n        } else if (!ValueHelpers.correspondsTo(containingValueType, getAspect().getValueType())) {\n            validator.addError(Component.translatable(L10NValues.ASPECT_ERROR_INVALIDTYPE,\n                    Component.translatable(containingValueType.getTranslationKey()),\n                    Component.translatable(getAspect().getValueType().getTranslationKey())));\n        }\n    }\n\n    @Override\n    public IValueType getOutputType() {\n        IAspect aspect = getAspect();\n        if(aspect == null) return null;\n        return aspect.getValueType();\n    }\n\n    @Override\n    protected IVariableFacadeClient constructClient() {\n        return new AspectVariableFacadeClient(this);\n    }\n\n    @Override\n    public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context) {\n        if(isValid()) {\n            getAspect().loadTooltip(tooltipAdder, false);\n            tooltipAdder.accept(Component.translatable(L10NValues.ASPECT_TOOLTIP_PARTID, getPartId()));\n        }\n        super.appendHoverText(tooltipAdder, context);\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        if (!super.equals(o)) return false;\n        AspectVariableFacade that = (AspectVariableFacade) o;\n        return partId == that.partId && Objects.equals(aspect, that.aspect);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), partId, aspect);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/AspectVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.client.renderer.item.CompositeModel;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviders;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic class AspectVariableFacadeClient implements IVariableFacadeClient {\n\n    private final AspectVariableFacade variableFacade;\n\n    public AspectVariableFacadeClient(AspectVariableFacade variableFacade) {\n        this.variableFacade = variableFacade;\n    }\n\n    @Override\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked) {\n        if(this.variableFacade.isValid()) {\n            IAspect aspect = this.variableFacade.getAspect();\n            IValueType valueType = aspect.getValueType();\n            return new CompositeModel(List.of(\n                    variableModelBaked.getSubModels(VariableModelProviders.VALUETYPE).getBakedModels().get(valueType),\n                    variableModelBaked.getSubModels(VariableModelProviders.ASPECT).getBakedModels().get(aspect)\n            ));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/DelayVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IDelayVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\n/**\n * Variable facade for variables determined by delays.\n * @author rubensworks\n */\npublic class DelayVariableFacade extends ProxyVariableFacade implements IDelayVariableFacade {\n\n    public DelayVariableFacade(boolean generateId, int proxyId) {\n        super(generateId, proxyId);\n    }\n\n    public DelayVariableFacade(int id, int proxyId) {\n        super(id, proxyId);\n    }\n\n    @Override\n    protected IVariableFacadeClient constructClient() {\n        return new DelayVariableFacadeClient(this);\n    }\n\n    @Override\n    protected MutableComponent getProxyNotInNetworkError() {\n        return Component.translatable(L10NValues.DELAY_ERROR_DELAYNOTINNETWORK, Integer.toString(getProxyId()));\n    }\n\n    @Override\n    protected MutableComponent getProxyInvalidError() {\n        return Component.translatable(L10NValues.DELAY_ERROR_DELAYINVALID, Integer.toString(getProxyId()));\n    }\n\n    @Override\n    protected MutableComponent getProxyInvalidTypeError(IPartNetwork network,\n                                                                     IValueType containingValueType,\n                                                                     IValueType actualType) {\n        return Component.translatable(L10NValues.DELAY_ERROR_DELAYINVALIDTYPE,\n                Component.translatable(containingValueType.getTranslationKey()),\n                Component.translatable(actualType.getTranslationKey()));\n    }\n\n    protected Component getProxyTooltip() {\n        return Component.translatable(L10NValues.DELAY_TOOLTIP_DELAYID, getProxyId());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/DelayVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviders;\n\nimport javax.annotation.Nullable;\n\n/**\n * @author rubensworks\n */\npublic class DelayVariableFacadeClient implements IVariableFacadeClient {\n\n    private final DelayVariableFacade variableFacade;\n\n    public DelayVariableFacadeClient(DelayVariableFacade variableFacade) {\n        this.variableFacade = variableFacade;\n    }\n\n    @Nullable\n    @Override\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked) {\n        if(this.variableFacade.isValid()) {\n            return variableModelBaked.getSubModels(VariableModelProviders.DELAY).getBakedModel();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/DummyVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\n\nimport javax.annotation.Nullable;\n\n/**\n * @author rubensworks\n */\npublic class DummyVariableFacadeClient implements IVariableFacadeClient {\n\n    @Override\n    @Nullable\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked) {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ItemBlockEnergyContainer.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.Mth;\nimport net.minecraft.world.item.BlockItem;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.component.TooltipDisplay;\nimport net.minecraft.world.level.block.Block;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.item.IInformationProvider;\nimport org.cyclops.cyclopscore.item.ItemBlockNBT;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.capability.energystorage.EnergyStorageItemBlockEnergyContainer;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Locale;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\n/**\n * {@link BlockItem} that can be used for blocks that implement the {@link EnergyHandler} capability.\n * Instances of this will also keep it's energy level.\n * @author rubensworks\n *\n */\npublic class ItemBlockEnergyContainer extends ItemBlockNBT {\n\n    private Block block;\n\n    public ItemBlockEnergyContainer(Block block, Properties builder) {\n        super(block, builder);\n        this.block = block;\n    }\n\n    /**\n     * @return The energy container.\n     */\n    public Block get() {\n        return block;\n    }\n\n    public Optional<EnergyHandler> getEnergyBattery(ItemStack itemStack) {\n        return Optional.ofNullable(itemStack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(itemStack)));\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n        getEnergyBattery(itemStack)\n                .ifPresent(energyStorage -> {\n                    long amount = energyStorage.getAmountAsLong();\n                    long capacity = energyStorage.getCapacityAsLong();\n                    String line = String.format(Locale.ROOT, \"%,d\", amount) + \" / \" + String.format(Locale.ROOT, \"%,d\", capacity) + \" \" + IModHelpers.get().getL10NHelpers().localize(L10NValues.GENERAL_ENERGY_UNIT);\n                    tooltipAdder.accept(Component.literal(IInformationProvider.ITEM_PREFIX + line));\n                });\n    }\n\n    @Override\n    public boolean isBarVisible(ItemStack stack) {\n        return true;\n    }\n\n    @Override\n    public int getBarWidth(ItemStack itemStack) {\n        return getEnergyBattery(itemStack)\n                .map(energyStorage -> {\n                    double amount = energyStorage.getAmountAsLong();\n                    double capacity = energyStorage.getCapacityAsLong();\n                    return (int) Math.round(amount / capacity * 13);\n                })\n                .orElse(0);\n    }\n\n    @Override\n    public int getBarColor(ItemStack stack) {\n        return Mth.hsvToRgb(Math.max(0.0F, ((float) getBarWidth(stack)) / 13) / 3.0F, 1.0F, 1.0F);\n    }\n\n    public EnergyStorageItemBlockEnergyContainer createCapability(ItemStack itemStack, ItemAccess itemAccess) {\n        return new EnergyStorageItemBlockEnergyContainer(this, itemStack, itemAccess) {\n            @Override\n            public int getRate() {\n                return BlockEntityEnergyBattery.getEnergyPerTick(getCapacityAsLong());\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ItemBlockEnergyContainerAutoSupply.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EquipmentSlot;\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.item.component.TooltipDisplay;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandlerUtil;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Consumer;\n\n/**\n * @author rubensworks\n */\npublic class ItemBlockEnergyContainerAutoSupply extends ItemBlockEnergyContainer {\n\n    public ItemBlockEnergyContainerAutoSupply(Block block, Properties builder) {\n        super(block, builder);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n        IModHelpers.get().getL10NHelpers().addStatusInfo(tooltipAdder, isActivated(itemStack), getDescriptionId() + \".info.auto_supply\");\n    }\n\n    @Override\n    public InteractionResult use(Level world, Player player, InteractionHand hand) {\n        return InteractionResult.SUCCESS.heldItemTransformedTo(toggleActivation(player.getItemInHand(hand), world, player));\n    }\n\n    public static void autofill(EnergyHandler source, Level world, Entity entity) {\n        if(entity instanceof Player && !world.isClientSide()) {\n            int tickAmount;\n            try (var tx = Transaction.openRoot()) {\n                tickAmount = source.extract(Integer.MAX_VALUE, tx);\n            }\n            if(tickAmount > 0) {\n                Player player = (Player) entity;\n                for (InteractionHand hand : InteractionHand.values()) {\n                    tryFillContainerForPlayer(source, ItemAccess.forPlayerInteraction(player, hand), tickAmount);\n                }\n            }\n        }\n    }\n\n    public static boolean tryFillContainerForPlayer(EnergyHandler source, ItemAccess held, int tickAmount) {\n        EnergyHandler target = held.getCapability(Capabilities.Energy.ITEM);\n        if (target != null) {\n            int moved;\n            try (var tx = Transaction.openRoot()) {\n                moved = EnergyHandlerUtil.move(source, target, tickAmount, tx);\n                tx.commit();\n            }\n            if (moved > 0) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public void inventoryTick(ItemStack itemStack, ServerLevel world, Entity entity, @Nullable EquipmentSlot slot) {\n        if (isActivated(itemStack)) {\n            EnergyHandler energyStorage = itemStack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(itemStack));\n            if (energyStorage != null) {\n                autofill(energyStorage, world, entity);\n            }\n        }\n        super.inventoryTick(itemStack, world, entity, slot);\n    }\n\n    public ItemStack toggleActivation(ItemStack itemStack, Level world, Player player) {\n        if(player.isSecondaryUseActive()) {\n            if(!world.isClientSide()) {\n                ItemStack activated = itemStack.copy();\n                activated.set(RegistryEntries.DATACOMPONENT_ACTIVATED, !activated.getOrDefault(RegistryEntries.DATACOMPONENT_ACTIVATED, false));\n                return activated;\n            }\n            return itemStack;\n        }\n        return itemStack;\n    }\n\n    public boolean isActivated(ItemStack itemStack) {\n        return itemStack.getOrDefault(RegistryEntries.DATACOMPONENT_ACTIVATED, false);\n    }\n\n    @Override\n    public boolean isFoil(ItemStack itemStack) {\n        return isActivated(itemStack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ItemPart.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\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.item.component.TooltipDisplay;\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.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.item.ItemBlockCable;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * An item that can place parts.\n * @author rubensworks\n */\npublic class ItemPart<P extends IPartType<P, S>, S extends IPartState<P>> extends Item {\n\n    private static final List<IUseAction> USE_ACTIONS = Lists.newArrayList();\n\n    private final IPartType<P, S> part;\n\n    public ItemPart(Item.Properties properties, IPartType<P, S> part) {\n        super(properties);\n        this.part = part;\n    }\n\n    public IPartType<P, S> getPart() {\n        return part;\n    }\n\n    /**\n     * Register a use action for the cable item.\n     * @param useAction The use action.\n     */\n    public static void addUseAction(IUseAction useAction) {\n        USE_ACTIONS.add(useAction);\n    }\n\n    @Override\n    public Component getName(ItemStack p_200295_1_) {\n        return Component.translatable(getDescriptionId());\n    }\n\n    @Override\n    public InteractionResult useOn(UseOnContext context) {\n        Level world = context.getLevel();\n        Player player = context.getPlayer();\n        InteractionHand hand = context.getHand();\n        BlockPos pos = context.getClickedPos();\n        Direction side = context.getClickedFace();\n        BlockState blockState = world.getBlockState(pos);\n\n        ItemStack itemStack = player.getItemInHand(hand);\n        IPartContainer partContainerFirst = PartHelpers.getPartContainer(world, pos, side, blockState).orElse(null);\n        if(partContainerFirst != null) {\n            // Add part to existing cable\n            if(PartHelpers.addPart(world, pos, side, getPart(), itemStack)) {\n                if(world.isClientSide()) {\n                    ItemBlockCable.playPlaceSound(world, pos);\n                }\n                if(!player.isCreative()) {\n                    itemStack.shrink(1);\n                }\n            }\n            return InteractionResult.SUCCESS;\n        } else {\n            // Place part at a new position with an unreal cable\n            BlockPos target = pos.relative(side);\n            Direction targetSide = side.getOpposite();\n            BlockHitResult targetRayTrace = new BlockHitResult(new Vec3(\n                    (double) target.getX() + 0.5D + (double) targetSide.getStepX() * 0.5D,\n                    (double) target.getY() + 0.5D + (double) targetSide.getStepY() * 0.5D,\n                    (double) target.getZ() + 0.5D + (double) targetSide.getStepZ() * 0.5D),\n                    targetSide, target, false);\n            if(world.getBlockState(target).canBeReplaced(new BlockPlaceContext(world, player, hand, itemStack, targetRayTrace.withPosition(target)))) {\n                ItemBlockCable itemBlockCable = (ItemBlockCable) Item.byBlock(RegistryEntries.BLOCK_CABLE.get());\n                itemStack.grow(1); // Temporarily grow, because ItemBlock will shrink it.\n                if (itemBlockCable.useOn(new UseOnContext(player, hand, targetRayTrace)).consumesAction()) {\n                    BlockState targetBlockState = world.getBlockState(target);\n                    IPartContainer partContainer = PartHelpers.getPartContainer(world, target, targetSide, targetBlockState).orElse(null);\n                    if (partContainer != null) {\n                        ICableFakeable cableFakeable = CableHelpers.getCableFakeable(world, target, targetSide, targetBlockState).orElse(null);\n                        if(!world.isClientSide()) {\n                            PartHelpers.addPart(world, target, side.getOpposite(), getPart(), itemStack);\n                            if (cableFakeable != null) {\n                                CableHelpers.onCableRemoving(world, target, false, false, world.getBlockState(target), world.getBlockEntity(target));\n                                cableFakeable.setRealCable(false);\n                                CableHelpers.overrideCableRemovingConnections(world, target, CableHelpers.ALL_SIDES);\n                                CableHelpers.onCableRemoved(world, target);\n                            } else {\n                                IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"Tried to set a fake cable at a block that is not fakeable at %s\", target));\n                            }\n                        } else {\n                            cableFakeable.setRealCable(false);\n                        }\n                        itemStack.shrink(1);\n                        return InteractionResult.SUCCESS;\n                    }\n                }\n                itemStack.shrink(1); // Shrink manually if failed\n            } else {\n                BlockState targetBlockState = world.getBlockState(target);\n                IPartContainer partContainer = PartHelpers.getPartContainer(world, target, targetSide, targetBlockState).orElse(null);\n                if(partContainer != null) {\n                    // Edge-case: if the pos was a full network block (part of the same network as target), make sure that we disconnect this part of the network first\n                    if (!world.isClientSide() && NetworkHelpers.getNetwork(PartPos.of(world, pos, side)).isPresent() && partContainer.canAddPart(targetSide, getPart())) {\n                        CableHelpers.getCable(world, target, targetSide)\n                                .ifPresent(cable -> CableHelpers.disconnectCable(world, target, targetSide, cable, targetSide));\n                    }\n\n                    // Add part to existing cable\n                    if(PartHelpers.addPart(world, target, side.getOpposite(), getPart(), itemStack)) {\n                        if(world.isClientSide()) {\n                            ItemBlockCable.playPlaceSound(world, target);\n                        }\n                        if(!player.isCreative()) {\n                            itemStack.shrink(1);\n                        }\n                    }\n                    return InteractionResult.SUCCESS;\n                }\n            }\n\n            // Check third party actions if all else fails\n            for (IUseAction useAction : USE_ACTIONS) {\n                if (useAction.attempItemUseTarget(this, itemStack, world, pos, side)) {\n                    return InteractionResult.SUCCESS;\n                }\n            }\n        }\n        return super.useOn(context);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        getPart().loadTooltip(itemStack, tooltipAdder);\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\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        ItemPart<?, ?> itemPart = (ItemPart<?, ?>) o;\n        return Objects.equals(part, itemPart.part);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(part);\n    }\n\n    public static interface IUseAction {\n\n        /**\n         * Attempt to use the given item.\n         * @param itemPart The part item instance.\n         * @param itemStack The item stack that is being used.\n         * @param world The world.\n         * @param pos The position.\n         * @param sideHit The side that is being hit.\n         * @return If the use action was applied.\n         */\n        public boolean attempItemUseTarget(ItemPart itemPart, ItemStack itemStack, Level world, BlockPos pos, Direction sideHit);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/OperatorVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.IExpression;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IOperatorVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.core.evaluate.expression.LazyExpression;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Arrays;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * Variable facade for variables determined for operators based on other variables in the network determined by their id.\n * @author rubensworks\n */\npublic class OperatorVariableFacade extends VariableFacadeBase implements IOperatorVariableFacade {\n\n    private final IOperator operator;\n    private final int[] variableIds;\n    private IExpression expression = null;\n    private int lastNetworkHash = -1;\n\n    // Flags to detect infinite recursion\n    private final boolean[] validatingVariables;\n    private final boolean[] variables;\n\n    public OperatorVariableFacade(boolean generateId, IOperator operator, int[] variableIds) {\n        super(generateId);\n        this.operator = operator;\n        this.variableIds = variableIds;\n        this.validatingVariables = this.variableIds != null ? new boolean[this.variableIds.length] : null;\n        this.variables = this.variableIds != null ? new boolean[this.variableIds.length] : null;\n    }\n\n    public OperatorVariableFacade(int id, IOperator operator, int[] variableIds) {\n        super(id);\n        this.operator = operator;\n        this.variableIds = variableIds;\n        this.validatingVariables = this.variableIds != null ? new boolean[this.variableIds.length] : null;\n        this.variables = this.variableIds != null ? new boolean[this.variableIds.length] : null;\n    }\n\n    public IOperator getOperator() {\n        return operator;\n    }\n\n    public int[] getVariableIds() {\n        return variableIds;\n    }\n\n    public IExpression getExpression() {\n        return expression;\n    }\n\n    public void setExpression(IExpression expression) {\n        this.expression = expression;\n    }\n\n    public int getLastNetworkHash() {\n        return lastNetworkHash;\n    }\n\n    public void setLastNetworkHash(int lastNetworkHash) {\n        this.lastNetworkHash = lastNetworkHash;\n    }\n\n    public boolean[] getValidatingVariables() {\n        return validatingVariables;\n    }\n\n    public boolean[] getVariables() {\n        return variables;\n    }\n\n    @Override\n    public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork) {\n        if(isValid()) {\n            int newNetworkHash = network != null ? network.hashCode() : -1;\n            if(expression == null || expression.hasErrored() || newNetworkHash != this.lastNetworkHash) {\n                this.lastNetworkHash = newNetworkHash;\n                IVariable[] variables = new IVariable[variableIds.length];\n                for (int i = 0; i < variableIds.length; i++) {\n                    int variableId = variableIds[i];\n                    if (!partNetwork.hasVariableFacade(variableId)) {\n                        return null;\n                    }\n                    IVariableFacade variableFacade = partNetwork.getVariableFacade(variableId);\n                    if(!variableFacade.isValid() || variableFacade == this) {\n                        return null;\n                    }\n                    if (this.variables[i]) {\n                        return null;\n                    }\n                    this.variables[i] = true;\n                    variables[i] = variableFacade.getVariable(network, partNetwork);\n                    this.variables[i] = false;\n                    if(variables[i] == null) {\n                        return null;\n                    }\n                }\n                expression = new LazyExpression(getId(), operator, variables, partNetwork);\n            }\n            return expression;\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isValid() {\n        return getVariableIds() != null && getOperator() != null;\n    }\n\n    @Override\n    public void validate(INetwork network, IPartNetwork partNetwork, final IValidator validator, IValueType containingValueType) {\n        if(!isValid()) {\n            validator.addError(Component.translatable(L10NValues.VARIABLE_ERROR_INVALIDITEM));\n        } else {\n            IValueType[] valueTypes = new IValueType[variableIds.length];\n            IVariable[] variables = new IVariable[variableIds.length];\n            boolean checkFurther = true;\n            for (int i = 0; i < variableIds.length; i++) {\n                int variableId = variableIds[i];\n                // Check valid id\n                if (variableId < 0) {\n                    validator.addError(Component.translatable(L10NValues.VARIABLE_ERROR_INVALIDITEM));\n                    checkFurther = false;\n                } else if (!partNetwork.hasVariableFacade(variableId)) { // Check id present in network\n                    validator.addError(Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK,\n                            Integer.toString(variableId)));\n                    checkFurther = false;\n                } else {\n                    // Check variable represented by this id is valid.\n                    IVariableFacade variableFacade = partNetwork.getVariableFacade(variableId);\n                    if(variableFacade == this) {\n                        validator.addError(Component.translatable(L10NValues.OPERATOR_ERROR_CYCLICREFERENCE,\n                                Integer.toString(variableId)));\n                        checkFurther = false;\n                    } else if (variableFacade != null) {\n                        IValueType valueType = getOperator().getInputTypes()[i];\n                        final Wrapper<Boolean> isValid = new Wrapper<>(true);\n                        if (validatingVariables[i]) {\n                            validator.addError(Component.translatable(\n                                    L10NValues.OPERATOR_ERROR_CYCLICREFERENCE, getId()));\n                            checkFurther = false;\n                            break;\n                        }\n                        validatingVariables[i] = true;\n                        variableFacade.validate(network, partNetwork, new IValidator() {\n                            @Override\n                            public void addError(MutableComponent error) {\n                                validator.addError(error);\n                                isValid.set(false);\n                            }\n                        }, valueType);\n                        validatingVariables[i] = false;\n                        if (isValid.get()) {\n                            IVariable variable = variableFacade.getVariable(network, partNetwork);\n                            if (variable != null) {\n                                variables [i] = variable;\n                                valueTypes[i] = variable.getType();\n                            }\n                        } else {\n                            checkFurther = false;\n                        }\n                    }\n                }\n            }\n            if(checkFurther) {\n                // Check operator validity\n                IOperator op = getOperator();\n                MutableComponent error = op.validateTypes(valueTypes);\n                if (error != null) {\n                    validator.addError(error);\n                }\n                // Check expected aspect type and operator output type\n                IValueType outputType = op.getConditionalOutputType(variables);\n                if (!ValueHelpers.correspondsTo(outputType, containingValueType)) {\n                    validator.addError(Component.translatable(L10NValues.ASPECT_ERROR_INVALIDTYPE,\n                            Component.translatable(containingValueType.getTranslationKey()),\n                            Component.translatable(outputType.getTranslationKey())));\n                }\n            }\n        }\n    }\n\n    @Override\n    public IValueType getOutputType() {\n        IOperator operator = getOperator();\n        if(operator == null) return null;\n        return operator.getOutputType();\n    }\n\n    @Override\n    protected IVariableFacadeClient constructClient() {\n        return new OperatorVariableFacadeClient(this);\n    }\n\n    @Override\n    public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context) {\n        if(isValid()) {\n            getOperator().loadTooltip(tooltipAdder, false);\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"{\");\n            boolean first = true;\n            for(int variableId : getVariableIds()) {\n                if(!first) {\n                    sb.append(\",\");\n                }\n                sb.append(getReferenceDisplay(variableId));\n                first = false;\n            }\n            sb.append(\"}\");\n            tooltipAdder.accept(Component.translatable(L10NValues.OPERATOR_TOOLTIP_VARIABLEIDS, sb.toString()));\n        }\n        super.appendHoverText(tooltipAdder, context);\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        if (!super.equals(o)) return false;\n        OperatorVariableFacade that = (OperatorVariableFacade) o;\n        return lastNetworkHash == that.lastNetworkHash && Objects.equals(operator, that.operator) && Objects.deepEquals(variableIds, that.variableIds) && Objects.equals(expression, that.expression) && Objects.deepEquals(validatingVariables, that.validatingVariables) && Objects.deepEquals(variables, that.variables);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), operator, Arrays.hashCode(variableIds), expression, lastNetworkHash, Arrays.hashCode(validatingVariables), Arrays.hashCode(variables));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/OperatorVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviders;\n\n/**\n * @author rubensworks\n */\npublic class OperatorVariableFacadeClient implements IVariableFacadeClient {\n\n    private final OperatorVariableFacade variableFacade;\n\n    public OperatorVariableFacadeClient(OperatorVariableFacade variableFacade) {\n        this.variableFacade = variableFacade;\n    }\n\n    @Override\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked) {\n        if(this.variableFacade.isValid()) {\n            IValueType valueType = this.variableFacade.getOperator().getOutputType();\n            return variableModelBaked.getSubModels(VariableModelProviders.VALUETYPE).getBakedModels().get(valueType);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ProxyVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IProxyVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\n/**\n * Variable facade for variables determined by proxies.\n * @author rubensworks\n */\npublic class ProxyVariableFacade extends VariableFacadeBase implements IProxyVariableFacade {\n\n    private final int proxyId;\n    private boolean isValidatingVariable = false;\n    private boolean isGettingVariable = false;\n\n    public ProxyVariableFacade(boolean generateId, int proxyId) {\n        super(generateId);\n        this.proxyId = proxyId;\n    }\n\n    public ProxyVariableFacade(int id, int proxyId) {\n        super(id);\n        this.proxyId = proxyId;\n    }\n\n    public int getProxyId() {\n        return proxyId;\n    }\n\n    public boolean isValidatingVariable() {\n        return isValidatingVariable;\n    }\n\n    public void setValidatingVariable(boolean validatingVariable) {\n        isValidatingVariable = validatingVariable;\n    }\n\n    public boolean isGettingVariable() {\n        return isGettingVariable;\n    }\n\n    public void setGettingVariable(boolean gettingVariable) {\n        isGettingVariable = gettingVariable;\n    }\n\n    protected Optional<BlockEntityProxy> getProxy(IPartNetwork network) {\n        DimPos dimPos = network.getProxy(proxyId);\n        if(dimPos != null) {\n            Level level = dimPos.getLevel(false);\n            if (level != null) {\n                return IModHelpers.get().getBlockEntityHelpers().get(level, dimPos.getBlockPos(), BlockEntityProxy.class);\n            }\n        }\n        return Optional.empty();\n    }\n\n    protected Optional<IVariable> getTargetVariable(IPartNetwork network) {\n        return getProxy(network)\n                .map(tile -> tile.getVariable(network));\n    }\n\n    @Override\n    public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork) {\n        if(isValid()) {\n            // Check if we are entering an infinite recursion (e.g. proxies refering to each other)\n            if(this.isGettingVariable) {\n                throw new VariableRecursionException(\"Detected infinite recursion for variable references.\");\n            }\n            this.isGettingVariable = true;\n            IVariable<V> variable = getTargetVariable(partNetwork).orElse(null);\n            this.isGettingVariable = false;\n            return variable;\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isValid() {\n        return proxyId >= 0;\n    }\n\n    protected MutableComponent getProxyNotInNetworkError() {\n        return Component.translatable(L10NValues.PROXY_ERROR_PROXYNOTINNETWORK, Integer.toString(proxyId));\n    }\n\n    protected MutableComponent getProxyInvalidError() {\n        return Component.translatable(L10NValues.PROXY_ERROR_PROXYINVALID, Integer.toString(proxyId));\n    }\n\n    protected MutableComponent getProxyInvalidTypeError(IPartNetwork network,\n                                                                     IValueType containingValueType,\n                                                                     IValueType actualType) {\n        return Component.translatable(L10NValues.PROXY_ERROR_PROXYINVALIDTYPE,\n                Integer.toString(proxyId),\n                Component.translatable(containingValueType.getTranslationKey()),\n                Component.translatable(actualType.getTranslationKey()));\n    }\n\n    @Override\n    public void validate(INetwork network, IPartNetwork partNetwork, IValidator validator, IValueType containingValueType) {\n        Optional<IVariable> targetVariable = getTargetVariable(partNetwork);\n        if (!isValid()) {\n            validator.addError(Component.translatable(L10NValues.VARIABLE_ERROR_INVALIDITEM));\n        } else if (partNetwork.getProxy(proxyId) == null) {\n            validator.addError(getProxyNotInNetworkError());\n        } else if (!targetVariable.isPresent()) {\n            validator.addError(getProxyInvalidError());\n        } else if (!ValueHelpers.correspondsTo(containingValueType, targetVariable.get().getType())) {\n            validator.addError(getProxyInvalidTypeError(partNetwork, containingValueType,\n                    targetVariable.get().getType()));\n        }\n\n        // Check if we are entering an infinite recursion (e.g. proxies referring to each other)\n        if(this.isValidatingVariable) {\n            throw new VariableRecursionException(\"Detected infinite recursion for variable references.\");\n        }\n        this.isValidatingVariable = true;\n        getVariable(network, partNetwork);\n        this.isValidatingVariable = false;\n    }\n\n    @Override\n    public IValueType getOutputType() {\n        return ValueTypes.CATEGORY_ANY;\n    }\n\n    protected Component getProxyTooltip() {\n        return Component.translatable(L10NValues.PROXY_TOOLTIP_PROXYID, proxyId);\n    }\n\n    @Override\n    protected IVariableFacadeClient constructClient() {\n        return new ProxyVariableFacadeClient(this);\n    }\n\n    @Override\n    public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context) {\n        if(isValid()) {\n            tooltipAdder.accept(getProxyTooltip());\n        }\n        super.appendHoverText(tooltipAdder, context);\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        if (!super.equals(o)) return false;\n        ProxyVariableFacade that = (ProxyVariableFacade) o;\n        return proxyId == that.proxyId && isValidatingVariable == that.isValidatingVariable && isGettingVariable == that.isGettingVariable;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(super.hashCode(), proxyId, isValidatingVariable, isGettingVariable);\n    }\n\n    public static class VariableRecursionException extends IllegalArgumentException {\n\n        public VariableRecursionException(String msg) {\n            super(msg);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ProxyVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.client.renderer.item.ItemModel;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviders;\n\n/**\n * @author rubensworks\n */\npublic class ProxyVariableFacadeClient implements IVariableFacadeClient {\n\n    private final ProxyVariableFacade variableFacade;\n\n    public ProxyVariableFacadeClient(ProxyVariableFacade variableFacade) {\n        this.variableFacade = variableFacade;\n    }\n\n    @Override\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked) {\n        if(this.variableFacade.isValid()) {\n            return variableModelBaked.getSubModels(VariableModelProviders.PROXY).getBakedModel();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ValueTypeVariableFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.Item;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.Variable;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.function.Consumer;\n\n/**\n * Variable facade for variables determined by a raw value.\n *\n * @author rubensworks\n */\npublic class ValueTypeVariableFacade<V extends IValue> extends VariableFacadeBase implements IValueTypeVariableFacade<V> {\n\n    private final IValueType<V> valueType;\n    private final V value;\n    private IVariable<V> variable = null;\n\n    public ValueTypeVariableFacade(boolean generateId, IValueType<V> valueType, V value) {\n        super(generateId);\n        this.valueType = valueType;\n        this.value = value;\n    }\n\n    public ValueTypeVariableFacade(int id, IValueType<V> valueType, V value) {\n        super(id);\n        this.valueType = valueType;\n        this.value = value;\n    }\n\n    @Override\n    public IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork) {\n        if (isValid()) {\n            if (variable == null) {\n                variable = new Variable<V>(getValueType(), getValue());\n            }\n            return variable;\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isValid() {\n        return getValueType() != null && getValue() != null;\n    }\n\n    @Override\n    public void validate(INetwork network, IPartNetwork partNetwork, IValidator validator, IValueType containingValueType) {\n        if (!isValid()) {\n            validator.addError(Component.translatable(L10NValues.VARIABLE_ERROR_INVALIDITEM));\n        } else {\n            // Check expected aspect type and operator output type\n            if (!ValueHelpers.correspondsTo(getValueType(), containingValueType)) {\n                validator.addError(Component.translatable(L10NValues.ASPECT_ERROR_INVALIDTYPE,\n                        Component.translatable(containingValueType.getTranslationKey()),\n                        Component.translatable(getValueType().getTranslationKey())));\n            }\n        }\n    }\n\n    @Override\n    public IValueType getOutputType() {\n        return getValueType();\n    }\n\n    @Override\n    protected IVariableFacadeClient constructClient() {\n        return new ValueTypeVariableFacadeClient<>(this);\n    }\n\n    @Override\n    public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context) {\n        if (isValid()) {\n            V value = getValue();\n            getValueType().loadTooltip(tooltipAdder, false, value);\n            tooltipAdder.accept(Component.translatable(L10NValues.VALUETYPE_TOOLTIP_VALUE, getValueType().toCompactString(value)));\n        }\n        super.appendHoverText(tooltipAdder, context);\n    }\n\n    public IValueType<V> getValueType() {\n        return this.valueType;\n    }\n\n    public V getValue() {\n        return this.value;\n    }\n\n    public IVariable<V> getVariable() {\n        return this.variable;\n    }\n\n    public void setVariable(IVariable<V> variable) {\n        this.variable = variable;\n    }\n\n    public String toString() {\n        return \"ValueTypeVariableFacade(valueType=\" + this.getValueType() + \", value=\" + this.getValue() + \", variable=\" + this.getVariable() + \")\";\n    }\n\n    public boolean equals(final Object o) {\n        if (o == this) return true;\n        if (!(o instanceof ValueTypeVariableFacade)) return false;\n        final ValueTypeVariableFacade<?> other = (ValueTypeVariableFacade<?>) o;\n        if (!other.canEqual((Object) this)) return false;\n        if (!super.equals(o)) return false;\n        final Object this$valueType = this.getValueType();\n        final Object other$valueType = other.getValueType();\n        if (this$valueType == null ? other$valueType != null : !this$valueType.equals(other$valueType)) return false;\n        final Object this$value = this.getValue();\n        final Object other$value = other.getValue();\n        if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;\n        final Object this$variable = this.getVariable();\n        final Object other$variable = other.getVariable();\n        if (this$variable == null ? other$variable != null : !this$variable.equals(other$variable)) return false;\n        return true;\n    }\n\n    protected boolean canEqual(final Object other) {\n        return other instanceof ValueTypeVariableFacade;\n    }\n\n    public int hashCode() {\n        final int PRIME = 59;\n        int result = super.hashCode();\n        final Object $valueType = this.getValueType();\n        result = result * PRIME + ($valueType == null ? 43 : $valueType.hashCode());\n        final Object $value = this.getValue();\n        result = result * PRIME + ($value == null ? 43 : $value.hashCode());\n        final Object $variable = this.getVariable();\n        result = result * PRIME + ($variable == null ? 43 : $variable.hashCode());\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/ValueTypeVariableFacadeClient.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.renderer.SubmitNodeCollector;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.world.item.ItemDisplayContext;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.client.model.IVariableModelBaked;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.core.client.model.VariableModelProviders;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeVariableFacadeClient<V extends IValue> implements IVariableFacadeClient {\n\n    private final ValueTypeVariableFacade<V> variableFacade;\n\n    public ValueTypeVariableFacadeClient(ValueTypeVariableFacade<V> variableFacade) {\n        this.variableFacade = variableFacade;\n    }\n\n    @Override\n    public ItemModel getItemModelOverlay(IVariableModelBaked variableModelBaked) {\n        if(this.variableFacade.isValid()) {\n            return variableModelBaked.getSubModels(VariableModelProviders.VALUETYPE).getBakedModels().get(this.variableFacade.getValueType());\n        }\n        return null;\n    }\n\n    @Override\n    public void renderISTER(ItemStack stack, ItemDisplayContext transformType, PoseStack matrixStack, SubmitNodeCollector submitNodeCollector, int combinedLight, int combinedOverlay) {\n        if(this.variableFacade.isValid()) {\n            this.variableFacade.getValueType().getClient().renderISTER(this.variableFacade.getValue(), stack, transformType, matrixStack, submitNodeCollector, combinedLight, combinedOverlay);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/VariableFacadeBase.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\n\nimport java.util.function.Consumer;\n\n/**\n * Base implementation of {@link IVariableFacade}\n *\n * @author rubensworks\n */\npublic abstract class VariableFacadeBase implements IVariableFacade {\n\n    private final int id;\n    private IVariableFacadeClient client;\n\n    public VariableFacadeBase(boolean generateId) {\n        this(generateId ? generateId() : -1);\n    }\n\n    public VariableFacadeBase(int id) {\n        this.id = id;\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            this.client = constructClient();\n        }\n    }\n\n    protected abstract IVariableFacadeClient constructClient();\n\n    public IVariableFacadeClient getClient() {\n        return client;\n    }\n\n    /**\n     * @return A unique new variable id.\n     */\n    public static int generateId() {\n        return IntegratedDynamics.globalCounters.get().getNext(\"variable\");\n    }\n\n    @Override\n    public String getLabel() {\n        return LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getLabel(getId());\n    }\n\n    protected String getReferenceDisplay(int variableId) {\n        String label = LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getLabel(variableId);\n        if (label == null) {\n            return String.valueOf(variableId);\n        } else {\n            return String.format(\"%s:%s\", label, variableId);\n        }\n    }\n\n    @Override\n    public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context) {\n        tooltipAdder.accept(Component.translatable(\"item.integrateddynamics.variable.id\", getId() == -1 ? \"...\" : getId()));\n    }\n\n    public int getId() {\n        return this.id;\n    }\n\n    public void setClient(IVariableFacadeClient client) {\n        this.client = client;\n    }\n\n    public boolean equals(final Object o) {\n        if (o == this) return true;\n        if (!(o instanceof VariableFacadeBase)) return false;\n        final VariableFacadeBase other = (VariableFacadeBase) o;\n        if (!other.canEqual((Object) this)) return false;\n        if (this.getId() != other.getId()) return false;\n        final Object this$client = this.getClient();\n        final Object other$client = other.getClient();\n        if (this$client == null ? other$client != null : !this$client.equals(other$client)) return false;\n        return true;\n    }\n\n    protected boolean canEqual(final Object other) {\n        return other instanceof VariableFacadeBase;\n    }\n\n    public int hashCode() {\n        final int PRIME = 59;\n        int result = 1;\n        result = result * PRIME + this.getId();\n        final Object $client = this.getClient();\n        result = result * PRIME + ($client == null ? 43 : $client.hashCode());\n        return result;\n    }\n\n    public String toString() {\n        return \"VariableFacadeBase(id=\" + this.getId() + \", client=\" + this.getClient() + \")\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/item/VariableFacadeHandlerRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.item;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.VariableAdapter;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeClient;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.event.LogicProgrammerVariableFacadeCreatedEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * The variable facade handler registry.\n * @author rubensworks\n */\npublic class VariableFacadeHandlerRegistry implements IVariableFacadeHandlerRegistry {\n\n    private static VariableFacadeHandlerRegistry INSTANCE = new VariableFacadeHandlerRegistry();\n    public static DummyVariableFacade DUMMY_FACADE = new DummyVariableFacade(L10NValues.VARIABLE_ERROR_INVALIDITEM);\n\n    private final Map<String, IVariableFacadeHandler> handlers = Maps.newHashMap();\n\n    private VariableFacadeHandlerRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static VariableFacadeHandlerRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public void registerHandler(IVariableFacadeHandler variableFacadeHandler) {\n        handlers.put(variableFacadeHandler.getUniqueName().toString(), variableFacadeHandler);\n    }\n\n    @Override\n    public IVariableFacade handle(ValueDeseralizationContext valueDeseralizationContext, ItemStack itemStack) {\n        if(itemStack.isEmpty() || !itemStack.has(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE)) {\n            return DUMMY_FACADE;\n        }\n        return handle(valueDeseralizationContext, itemStack.get(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE));\n    }\n\n    @Override\n    public IVariableFacade handle(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tagCompound) {\n        if(tagCompound == null) {\n            return DUMMY_FACADE;\n        }\n        if(!tagCompound.contains(\"_type\") || !(tagCompound.contains(\"_id\"))) {\n            return DUMMY_FACADE;\n        }\n        String type = tagCompound.getString(\"_type\").orElseThrow();\n        int id = tagCompound.getInt(\"_id\").orElseThrow();\n        IVariableFacadeHandler handler = getHandler(Identifier.parse(type));\n        if(handler != null) {\n            return handler.getVariableFacade(valueDeseralizationContext, id, tagCompound);\n        }\n        return DUMMY_FACADE;\n    }\n\n    @Nullable\n    @Override\n    public IVariableFacadeHandler getHandler(Identifier type) {\n        return handlers.get(type.toString());\n    }\n\n    @Override\n    public Collection<String> getHandlerNames() {\n        return handlers.keySet();\n    }\n\n    @Override\n    public <F extends IVariableFacade> void write(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tagCompound, F variableFacade, IVariableFacadeHandler<F> handler) {\n        tagCompound.putString(\"_type\", handler.getUniqueName().toString());\n        tagCompound.putInt(\"_id\", variableFacade.getId());\n        handler.setVariableFacade(valueDeseralizationContext, tagCompound, variableFacade);\n    }\n\n    @Override\n    public <F extends IVariableFacade> ItemStack writeVariableFacadeItem(ItemStack itemStack, F variableFacade, IVariableFacadeHandler<F> variableFacadeHandler) {\n        if(itemStack.isEmpty()) {\n            return ItemStack.EMPTY;\n        }\n        itemStack = itemStack.copy();\n        CompoundTag tag = new CompoundTag();\n        this.write(ValueDeseralizationContext.ofAllEnabled(), tag, variableFacade, variableFacadeHandler);\n        itemStack.set(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE, tag);\n        return itemStack;\n    }\n\n    @Override\n    public <F extends IVariableFacade> ItemStack writeVariableFacadeItem(boolean generateId, ItemStack itemStack, IVariableFacadeHandler<F> variableFacadeHandler, IVariableFacadeFactory<F> variableFacadeFactory, Level level, @Nullable Player player, @Nullable BlockState blockState) {\n        if(itemStack.isEmpty()) {\n            return ItemStack.EMPTY;\n        }\n        itemStack = itemStack.copy();\n        CompoundTag tag = new CompoundTag();\n        F variableFacade = writeVariableFacade(generateId, itemStack, variableFacadeHandler, variableFacadeFactory, ValueDeseralizationContext.of(level));\n        if (player != null) {\n            NeoForge.EVENT_BUS.post(new LogicProgrammerVariableFacadeCreatedEvent(player, variableFacade, blockState));\n        }\n        this.write(ValueDeseralizationContext.of(level), tag, variableFacade, variableFacadeHandler);\n        itemStack.set(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE, tag);\n        return itemStack;\n    }\n\n    @Override\n    public <F extends IVariableFacade> F writeVariableFacade(boolean generateId, ItemStack itemStack, IVariableFacadeHandler<F> variableFacadeHandler, IVariableFacadeFactory<F> variableFacadeFactory, ValueDeseralizationContext valueDeseralizationContext) {\n        if(itemStack.isEmpty()) {\n            return null;\n        }\n        CompoundTag tag = Objects.requireNonNullElseGet(itemStack.get(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE), CompoundTag::new).copy();\n        IVariableFacade previousVariableFacade = this.handle(valueDeseralizationContext, tag);\n        F variableFacade;\n        if(generateId && previousVariableFacade.getId() > -1) {\n            variableFacade = variableFacadeFactory.create(previousVariableFacade.getId());\n        } else {\n            variableFacade = variableFacadeFactory.create(generateId);\n        }\n        itemStack.set(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE, tag);\n        return variableFacade;\n    }\n\n    @Override\n    public <F extends IVariableFacade> ItemStack copy(boolean generateId, ItemStack itemStack) {\n        ItemStack copy = itemStack.copy();\n        int newId = generateId ? VariableFacadeBase.generateId() : -1;\n        CompoundTag tagCopy = itemStack.get(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE).copy();\n        tagCopy.putInt(\"_id\", newId);\n        copy.set(RegistryEntries.DATACOMPONENT_VARIABLE_FACADE, tagCopy);\n        return copy;\n    }\n\n    /**\n     * Variable facade used for items that have no (valid) information on them.\n     */\n    public static class DummyVariableFacade extends VariableFacadeBase {\n\n        private static final IVariable VARIABLE_TRUE = new VariableAdapter<ValueTypeBoolean.ValueBoolean>() {\n            @Override\n            public IValueType<ValueTypeBoolean.ValueBoolean> getType() {\n                return ValueTypes.BOOLEAN;\n            }\n\n            @Override\n            public ValueTypeBoolean.ValueBoolean getValue() throws EvaluationException {\n                return ValueTypeBoolean.ValueBoolean.of(true);\n            }\n        };\n\n        private final String unlocalizedError;\n\n        public DummyVariableFacade(String unlocalizedError) {\n            super(false);\n            this.unlocalizedError = unlocalizedError;\n        }\n\n        @Override\n        public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork) {\n            return VARIABLE_TRUE;\n        }\n\n        @Override\n        public boolean isValid() {\n            return false;\n        }\n\n        @Override\n        public void validate(INetwork network, IPartNetwork partNetwork, IValidator validator, IValueType containingValueType) {\n            if (!ValueHelpers.correspondsTo(containingValueType, ValueTypes.BOOLEAN)) {\n                validator.addError(Component.translatable(unlocalizedError));\n            }\n        }\n\n        @Override\n        public IValueType getOutputType() {\n            return ValueTypes.CATEGORY_ANY;\n        }\n\n        @Override\n        protected IVariableFacadeClient constructClient() {\n            return new DummyVariableFacadeClient();\n        }\n\n        @Override\n        public void appendHoverText(Consumer<Component> tooltipAdder, Item.TooltipContext context) {\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/IRenderPatternValueTypeTooltip.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\n/**\n * @author rubensworks\n */\npublic interface IRenderPatternValueTypeTooltip {\n\n    public default List<Component> getValueTypeTooltip(IValueType<?> valueType) {\n        List<Component> lines = Lists.newLinkedList();\n        lines.add(Component.translatable(valueType.getTranslationKey())\n                .withStyle(valueType.getDisplayColorFormat()));\n        return lines;\n    }\n\n    public abstract boolean isRenderTooltip();\n\n    public abstract void setRenderTooltip(boolean renderTooltip);\n\n    public default void drawTooltipForeground(ContainerScreenLogicProgrammerBase gui, GuiGraphicsExtractor guiGraphics, ContainerLogicProgrammerBase container, int guiLeft, int guiTop, int mouseX, int mouseY, IValueType valueType) {\n        if (isRenderTooltip()) {\n            // Output type tooltip\n            if (!container.hasWriteItemInSlot()) {\n                if (gui.isHovering(ContainerLogicProgrammerBase.OUTPUT_X, ContainerLogicProgrammerBase.OUTPUT_Y,\n                        ContainerScreenLogicProgrammerBase.BOX_HEIGHT, ContainerScreenLogicProgrammerBase.BOX_HEIGHT, mouseX, mouseY)\n                        && Minecraft.getInstance().player.containerMenu.getCarried().isEmpty()) {\n                    List<Component> tooltips = Lists.newArrayList(Component.translatable(L10NValues.GUI_LOGICPROGRAMMER_TOOLTIP_WRITESLOT_CREATE));\n                    tooltips = Stream.concat(tooltips.stream(), getValueTypeTooltip(valueType).stream()).toList();\n                    gui.drawTooltip(tooltips, guiGraphics, mouseX, mouseY);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/LogicProgrammerElementTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementTypeRegistry;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Registry for {@link ILogicProgrammerElementType}.\n * @author rubensworks\n */\npublic class LogicProgrammerElementTypeRegistry implements ILogicProgrammerElementTypeRegistry {\n\n    private static final LogicProgrammerElementTypeRegistry INSTANCE = new LogicProgrammerElementTypeRegistry();\n\n    private final Map<String, ILogicProgrammerElementType> namedTypes = Maps.newHashMap();\n    private final List<ILogicProgrammerElementType> types = Lists.newArrayList();\n\n    /**\n     * @return The unique instance.\n     */\n    public static LogicProgrammerElementTypeRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <E extends ILogicProgrammerElementType> E addType(E type) {\n        types.add(type);\n        namedTypes.put(type.getUniqueName().toString(), type);\n        return type;\n    }\n\n    @Override\n    public List<ILogicProgrammerElementType> getTypes() {\n        return Collections.unmodifiableList(types);\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType(Identifier name) {\n        return namedTypes.get(name.toString());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/LogicProgrammerElementTypes.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementTypeRegistry;\n\n/**\n * All types of logic programmer element types.\n * @author rubensworks\n */\npublic class LogicProgrammerElementTypes {\n\n    public static final ILogicProgrammerElementTypeRegistry REGISTRY = IntegratedDynamics._instance.getRegistryManager().getRegistry(ILogicProgrammerElementTypeRegistry.class);\n\n    public static void load() {}\n\n    public static final ValueTypeLPElementType VALUETYPE = REGISTRY.addType(new ValueTypeLPElementType());\n    public static final OperatorLPElementType OPERATOR  = REGISTRY.addType(new OperatorLPElementType());\n\n    public static boolean areEqual(ILogicProgrammerElement e1, ILogicProgrammerElement e2) {\n        if(e1 == null) {\n            return e2 == null;\n        }\n        if(e2 == null) {\n            return false;\n        }\n        return e1.getType().getUniqueName().equals(e2.getType().getUniqueName()) && e1.getType().getName(e1).equals(e2.getType().getName(e2));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/OperatorLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Iterables;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.ContainerInput;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeCategory;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IOperatorVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandler;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.item.OperatorVariableFacade;\nimport org.cyclops.integrateddynamics.core.item.ValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.OperatorLPElementClient;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\nimport java.util.function.Consumer;\n\n/**\n * Element for operator.\n *\n * @author rubensworks\n */\npublic class OperatorLPElement implements ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase, OperatorLPElementClient<?>> {\n\n    private final IOperator operator;\n    private IVariableFacade[] inputVariables;\n    private boolean focused = false;\n\n    private OperatorLPElementClient<?> client;\n\n    public OperatorLPElement(IOperator operator) {\n        this.operator = operator;\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            this.client = constructClient();\n        }\n    }\n\n    private OperatorLPElementClient<?> constructClient() {\n        return new OperatorLPElementClient<>(this);\n    }\n\n    @Override\n    public OperatorLPElementClient<?> getClient() {\n        return client;\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.OPERATOR;\n    }\n\n    @Override\n    public String getMatchString() {\n        return getOperator().getLocalizedNameFull().getString().toLowerCase();\n    }\n\n    @Override\n    public boolean matchesInput(IValueType valueType) {\n        for (IValueType operatorIn : getOperator().getInputTypes()) {\n            if (ValueHelpers.correspondsTo(operatorIn, valueType)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean matchesOutput(IValueType valueType) {\n        return ValueHelpers.correspondsTo(getOperator().getOutputType(), valueType);\n    }\n\n    @Override\n    public Component getName() {\n        return getOperator().getLocalizedNameFull();\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder) {\n        getOperator().loadTooltip(tooltipAdder, true);\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        if (getOperator().getRenderPattern() == null) {\n            throw new IllegalStateException(\"Tried to render a (possibly virtual) operator with a null render pattern: \"\n                    + getOperator().getUniqueName());\n        }\n        return getOperator().getRenderPattern();\n    }\n\n    @Override\n    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack) {\n        IVariableFacade variableFacade = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).handle(ValueDeseralizationContext.of(player.level()), itemStack);\n        inputVariables[slotId] = variableFacade;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        for (IVariableFacade inputVariable : inputVariables) {\n            if (inputVariable == null || !inputVariable.isValid()) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    protected int[] getVariableIds(IVariableFacade[] inputVariables) {\n        int[] variableIds = new int[inputVariables.length];\n        for (int i = 0; i < inputVariables.length; i++) {\n            variableIds[i] = inputVariables[i].getId();\n        }\n        return variableIds;\n    }\n\n    @Override\n    public ItemStack writeElement(Player player, ItemStack itemStack) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        int[] variableIds = getVariableIds(inputVariables);\n        return registry.writeVariableFacadeItem(!player.level().isClientSide(), itemStack, Operators.REGISTRY,\n                new OperatorVariableFacadeFactory(operator, variableIds), player.level(), player, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get().defaultBlockState());\n    }\n\n    @Override\n    public void loadElement(IVariableFacade variableFacade) {\n        if (variableFacade instanceof OperatorVariableFacade operatorVariableFacade) {\n            int[] variableIds = operatorVariableFacade.getVariableIds();\n            this.inputVariables = new IVariableFacade[variableIds.length];\n            for (int i = 0; i < variableIds.length; i++) {\n                IValueType valueType = operator.getInputTypes()[i];\n                if (valueType instanceof IValueTypeCategory<?> valueTypeCategory) {\n                    valueType = Iterables.getFirst(valueTypeCategory.getElements(), ValueTypes.BOOLEAN);\n                }\n                this.inputVariables[i] = new ValueTypeVariableFacade(variableIds[i], valueType, valueType.getDefault());\n            }\n        }\n    }\n\n    @Override\n    public void setValueInContainer(ContainerLogicProgrammerBase container) {\n        SimpleInventory inputSlots = container.getTemporaryInputSlots();\n        for (int i = 0; i < inputSlots.getContainerSize(); i++) {\n            ItemStack itemStack = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).writeVariableFacadeItem(new ItemStack(RegistryEntries.ITEM_VARIABLE), inputVariables[i], (IVariableFacadeHandler) ValueTypes.REGISTRY);\n            inputSlots.setItem(i, itemStack);\n        }\n    }\n\n    @Override\n    public boolean canCurrentlyReadFromOtherItem() {\n        return true;\n    }\n\n    @Override\n    public void activate() {\n        this.inputVariables = new IVariableFacade[getRenderPattern().getSlotPositions().length];\n    }\n\n    @Override\n    public void deactivate() {\n        this.inputVariables = null;\n    }\n\n    @Override\n    public Component validate() {\n        return getOperator().validateTypes(ValueHelpers.from(inputVariables));\n    }\n\n    @Override\n    public int getColor() {\n        return getOperator().getOutputType().getDisplayColor();\n    }\n\n    @Override\n    public String getSymbol() {\n        return getOperator().getSymbol();\n    }\n\n    @Override\n    public boolean isFor(IVariableFacade variableFacade) {\n        if (variableFacade instanceof IOperatorVariableFacade) {\n            IOperatorVariableFacade operatorFacade = (IOperatorVariableFacade) variableFacade;\n            if (operatorFacade.isValid()) {\n                return getOperator() == operatorFacade.getOperator();\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {\n        return itemStack.getItem() == RegistryEntries.ITEM_VARIABLE.get();\n    }\n\n    @Override\n    public boolean slotClick(int slotId, Slot slot, int mouseButton, ContainerInput clickType, Player player) {\n        return false;\n    }\n\n    @Override\n    public int getItemStackSizeLimit() {\n        return 1;\n    }\n\n    public IOperator getOperator() {\n        return this.operator;\n    }\n\n    public IVariableFacade[] getInputVariables() {\n        return this.inputVariables;\n    }\n\n    public void setInputVariables(IVariableFacade[] inputVariables) {\n        this.inputVariables = inputVariables;\n    }\n\n    public boolean isFocused() {\n        return focused;\n    }\n\n    public void setFocused(boolean focused) {\n        this.focused = focused;\n    }\n\n    public boolean equals(final Object o) {\n        if (o == this) return true;\n        if (!(o instanceof OperatorLPElement)) return false;\n        final OperatorLPElement other = (OperatorLPElement) o;\n        if (!other.canEqual((Object) this)) return false;\n        final Object this$operator = this.getOperator();\n        final Object other$operator = other.getOperator();\n        if (this$operator == null ? other$operator != null : !this$operator.equals(other$operator)) return false;\n        if (!java.util.Arrays.deepEquals(this.getInputVariables(), other.getInputVariables())) return false;\n        if (this.isFocused() != other.isFocused()) return false;\n        return true;\n    }\n\n    protected boolean canEqual(final Object other) {\n        return other instanceof OperatorLPElement;\n    }\n\n    public int hashCode() {\n        final int PRIME = 59;\n        int result = 1;\n        final Object $operator = this.getOperator();\n        result = result * PRIME + ($operator == null ? 43 : $operator.hashCode());\n        result = result * PRIME + java.util.Arrays.deepHashCode(this.getInputVariables());\n        result = result * PRIME + (this.isFocused() ? 79 : 97);\n        return result;\n    }\n\n    public String toString() {\n        return \"OperatorLPElement(operator=\" + this.getOperator() + \", inputVariables=\" + java.util.Arrays.deepToString(this.getInputVariables()) + \", focused=\" + this.isFocused() + \")\";\n    }\n\n    public static class OperatorVariableFacadeFactory implements IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IOperatorVariableFacade> {\n\n        private final IOperator operator;\n        private final int[] variableIds;\n\n        public OperatorVariableFacadeFactory(IOperator operator, int[] variableIds) {\n            this.operator = operator;\n            this.variableIds = variableIds;\n        }\n\n        @Override\n        public IOperatorVariableFacade create(boolean generateId) {\n            return new OperatorVariableFacade(generateId, operator, variableIds);\n        }\n\n        @Override\n        public IOperatorVariableFacade create(int id) {\n            return new OperatorVariableFacade(id, operator, variableIds);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/OperatorLPElementType.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\nimport java.util.List;\n\n/**\n * Operator element type.\n * @author rubensworks\n */\npublic class OperatorLPElementType implements ILogicProgrammerElementType<OperatorLPElement> {\n\n    @Override\n    public OperatorLPElement getByName(Identifier name) {\n        return new OperatorLPElement(Operators.REGISTRY.getOperator(name));\n    }\n\n    @Override\n    public Identifier getName(OperatorLPElement element) {\n        return element.getOperator().getUniqueName();\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"operator\");\n    }\n\n    @Override\n    public List<OperatorLPElement> createElements() {\n        List<OperatorLPElement> elements = Lists.newArrayList();\n        for(IOperator operator : Operators.REGISTRY.getOperators()) {\n            elements.add(new OperatorLPElement(operator));\n        }\n        return elements;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/RenderPatternCommon.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\n\n/**\n * @author rubensworks\n */\npublic class RenderPatternCommon {\n    public static int calculateX(int baseX, int maxWidth, IConfigRenderPattern configRenderPattern) {\n        return baseX + (maxWidth  - configRenderPattern.getWidth()) / 2;\n    }\n\n    public static int calculateY(int baseY, int maxHeight, IConfigRenderPattern configRenderPattern) {\n        return baseY + (maxHeight - configRenderPattern.getHeight()) / 2;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/SingleLPElementType.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\n\nimport java.util.List;\n\n/**\n * Element type that provides exactly one element.\n * @author rubensworks\n */\npublic class SingleLPElementType<E extends ILogicProgrammerElement> implements ILogicProgrammerElementType<E> {\n\n    private final ILogicProgrammerElementConstructor<E> constructor;\n    private final String id;\n\n    public SingleLPElementType(ILogicProgrammerElementConstructor<E> constructor, String id) {\n        this.constructor = constructor;\n        this.id = id;\n    }\n\n    @Override\n    public E getByName(Identifier name) {\n        return constructor.construct();\n    }\n\n    @Override\n    public Identifier getName(E element) {\n        return Identifier.parse(\"\");\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"single_\" + id);\n    }\n\n    @Override\n    public List<E> createElements() {\n        return ImmutableList.of(constructor.construct());\n    }\n\n    public static interface ILogicProgrammerElementConstructor<E extends ILogicProgrammerElement> {\n\n        public E construct();\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeBooleanLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeBooleanLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Element for the boolean value type that is controlled via a checkbox.\n * @author rubensworks\n */\npublic class ValueTypeBooleanLPElement extends ValueTypeLPElementBase<ValueTypeBooleanLPElementClient> {\n\n    private GuiElementValueTypeBoolean<ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> innerGuiElement;\n\n    public ValueTypeBooleanLPElement(IValueType valueType) {\n        super(valueType);\n        this.innerGuiElement = createInnerGuiElement();\n    }\n\n    @Nullable\n    @Override\n    public <G2 extends Screen, C2 extends AbstractContainerMenu> GuiElementValueTypeBoolean<G2, C2> createInnerGuiElement() {\n        return new GuiElementValueTypeBoolean<>((ValueTypeBoolean) getValueType(), getRenderPattern());\n    }\n\n    @Override\n    public ValueTypeBooleanLPElementClient constructClient() {\n        return new ValueTypeBooleanLPElementClient(this);\n    }\n\n    @Override\n    public GuiElementValueTypeBoolean<ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> getInnerGuiElement() {\n        return innerGuiElement;\n    }\n\n    @Override\n    public void activate() {\n        getInnerGuiElement().activate();\n    }\n\n    @Override\n    public void deactivate() {\n        getInnerGuiElement().deactivate();\n    }\n\n    @Override\n    public Component validate() {\n        return getInnerGuiElement().validate();\n    }\n\n    @Override\n    public IValue getValue() {\n        return getInnerGuiElement().getValue();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeIngredientsLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentHandler;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeIngredientsLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeIngredientsValueChangedPacket;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Element for the ingredients value type.\n *\n * @author rubensworks\n */\npublic class ValueTypeIngredientsLPElement extends ValueTypeLPElementBase<ValueTypeIngredientsLPElementClient> {\n\n    public static final int OFFSET_X = 20;\n    public static final int OFFSET_Y = 21;\n\n    private IngredientComponent currentType = IngredientComponent.ITEMSTACK;\n    private Map<IngredientComponent, Integer> lengths = Maps.newHashMap();\n    private Map<IngredientComponent, Map<Integer, IValueTypeLogicProgrammerElement>> subElements = Maps.newHashMap();\n    private int activeElement = -1;\n\n    private ValueObjectTypeIngredients.ValueIngredients serverValue = null;\n\n    public ValueTypeIngredientsLPElement() {\n        super(ValueTypes.OBJECT_INGREDIENTS);\n    }\n\n    public void setServerValue(ValueObjectTypeIngredients.ValueIngredients serverValue) {\n        this.serverValue = serverValue;\n    }\n\n    @Override\n    public ValueTypeIngredientsLPElementClient constructClient() {\n        return new ValueTypeIngredientsLPElementClient(this);\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.VALUETYPE;\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.NONE_CANVAS_WIDE;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return true;\n    }\n\n    protected IMixedIngredients constructValues() {\n        Map<IngredientComponent<?, ?>, List<?>> lists = Maps.newIdentityHashMap();\n        for (IngredientComponent<?, ?> component : IngredientComponentHandlers.REGISTRY.getComponents()) {\n            List values = Lists.newArrayListWithExpectedSize(lengths.get(component));\n            subElements.get(component).entrySet().forEach(entry -> {\n                IIngredientComponentHandler componentHandler = IngredientComponentHandlers.REGISTRY.getComponentHandler(component);\n                try {\n                    values.add(componentHandler.toInstance(entry.getValue().getValue()));\n                } catch (Exception e) {\n                    values.add(component.getMatcher().getEmptyInstance());\n                }\n            });\n            if (!values.isEmpty()) {\n                lists.put(component, values);\n            }\n        }\n        return new MixedIngredients(lists);\n    }\n\n    @Override\n    public IValue getValue() {\n        return IModHelpers.get().getMinecraftHelpers().isClientSideThread()\n                ? ValueObjectTypeIngredients.ValueIngredients.of(constructValues()) : serverValue;\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        ValueObjectTypeIngredients.ValueIngredients valueIngredients = (ValueObjectTypeIngredients.ValueIngredients) value;\n        if (!IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            setServerValue(valueIngredients);\n        }\n\n        valueIngredients.getRawValue().ifPresent(ingredients -> {\n            // Select itemstack by default if it has instances\n            if (ingredients.getComponents().contains(IngredientComponent.ITEMSTACK)) {\n                currentType = IngredientComponent.ITEMSTACK;\n            } else {\n                currentType = null;\n            }\n\n            for (IngredientComponent<?, ?> ingredientComponent : ingredients.getComponents()) {\n                IIngredientComponentHandler handler = IngredientComponentHandlers.REGISTRY.getComponentHandler(ingredientComponent);\n\n                // If no itemstacks in ingredient, select any other\n                if (currentType == null) {\n                    currentType = ingredientComponent;\n                }\n\n                // Save length per ingredient component\n                lengths.put(ingredientComponent, ingredients.getInstances(ingredientComponent).size());\n\n                // Initialize LP elements for all instances of this ingredient component\n                Map<Integer, IValueTypeLogicProgrammerElement> entries = Maps.newHashMap();\n                List<?> instances = ingredients.getInstances(ingredientComponent);\n                for (int i = 0; i < instances.size(); i++) {\n                    initializeElementFromInstanceValue(entries, handler, instances.get(i), i);\n                }\n                subElements.put(ingredientComponent, entries);\n            }\n        });\n    }\n\n    protected <VT extends IValueType<V>, V extends IValue, T, M> void initializeElementFromInstanceValue(Map<Integer, IValueTypeLogicProgrammerElement> entries, IIngredientComponentHandler<VT, V, T, M> handler, T instance, int instanceIndex) {\n        IValue instanceValue = handler.toValue(instance);\n        IValueTypeLogicProgrammerElement lpElement = instanceValue.getType().createLogicProgrammerElement();\n        lpElement.setValue(instanceValue);\n        entries.put(instanceIndex, lpElement);\n    }\n\n    @Override\n    public void setValueInContainer(ContainerLogicProgrammerBase container) {\n        if (!subElements.get(currentType).isEmpty()) {\n            IValueTypeLogicProgrammerElement subElement = setActiveElement(0);\n            int x = RenderPatternCommon.calculateX(ContainerLogicProgrammerBase.BASE_X, ContainerLogicProgrammerBase.MAX_WIDTH, subElement.getRenderPattern()) + ContainerLogicProgrammerBase.BASE_X - OFFSET_X;\n            int y = RenderPatternCommon.calculateY(ContainerLogicProgrammerBase.BASE_Y, ContainerLogicProgrammerBase.MAX_HEIGHT, subElement.getRenderPattern()) + ContainerLogicProgrammerBase.BASE_Y - OFFSET_Y;\n            container.setElementInventory(subElement, x, y);\n            subElement.setValueInContainer(container);\n        }\n    }\n\n    public int getLength() {\n        return lengths.get(currentType);\n    }\n\n    public void setLength(int length) {\n        lengths.put(currentType, length);\n        setActiveElement(getLength() - 1);\n    }\n\n    public IngredientComponent getCurrentType() {\n        return currentType;\n    }\n\n    public void setCurrentType(IngredientComponent currentType) {\n        this.currentType = currentType;\n        setActiveElement(subElements.get(currentType).size() - 1);\n    }\n\n    public int getActiveElement() {\n        return activeElement;\n    }\n\n    public IValueTypeLogicProgrammerElement setActiveElement(int index) {\n        activeElement = index;\n        IValueTypeLogicProgrammerElement subElement = null;\n        if (index >= 0) {\n            if (!subElements.get(currentType).containsKey(index)) {\n                subElements.get(currentType).put(index, subElement = IngredientComponentHandlers.REGISTRY.getComponentHandler(currentType)\n                        .getValueType().createLogicProgrammerElement());\n            } else {\n                subElement = subElements.get(currentType).get(index);\n            }\n        }\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            getClient().setActiveElement(activeElement);\n        }\n        return subElement;\n    }\n\n    public void removeElement(int index) {\n        Map<Integer, IValueTypeLogicProgrammerElement> oldSubElements = subElements.get(currentType);\n        subElements.put(currentType, Maps.newHashMap());\n        for (Map.Entry<Integer, IValueTypeLogicProgrammerElement> entry : oldSubElements.entrySet()) {\n            int i = entry.getKey();\n            if (i < index) {\n                subElements.get(currentType).put(i, entry.getValue());\n            } else if (i > index) {\n                subElements.get(currentType).put(i - 1, entry.getValue());\n            }\n        }\n        getClient().removeElement(index);\n        setLength(getLength() - 1);\n    }\n\n    public Map<IngredientComponent, Map<Integer, IValueTypeLogicProgrammerElement>> getSubElements() {\n        return subElements;\n    }\n\n    @Override\n    public void activate() {\n        for (IngredientComponent recipeComponent : IngredientComponentHandlers.REGISTRY.getComponents()) {\n            subElements.put(recipeComponent, Maps.newHashMap());\n            lengths.put(recipeComponent, 0);\n        }\n        getClient().activate();\n    }\n\n    @Override\n    public void deactivate() {\n\n    }\n\n    @Override\n    public Component validate() {\n        if (!IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            return serverValue == null ? Component.literal(\"\") : null;\n        }\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeIngredientsValueChangedPacket(ValueDeseralizationContext.ofClient(),\n                            ValueObjectTypeIngredients.ValueIngredients.of(constructValues())));\n        }\n        for (Map<Integer, IValueTypeLogicProgrammerElement> componentValues : subElements.values()) {\n            for (Map.Entry<Integer, IValueTypeLogicProgrammerElement> entry : componentValues.entrySet()) {\n                Component error = entry.getValue().validate();\n                if (error != null) {\n                    return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDLISTELEMENT, entry.getKey(), error);\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {\n        return (slotId == 0 && super.isItemValidForSlot(slotId, itemStack)) ||\n                (activeElement >= 0 && subElements.get(currentType).containsKey(activeElement)\n                        && subElements.get(currentType).get(activeElement).isItemValidForSlot(slotId, itemStack));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeItemStackLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeItemStackLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Element for a value type that can be derived from an {@link ItemStack}.\n * @author rubensworks\n */\npublic class ValueTypeItemStackLPElement<V extends IValue> extends ValueTypeLPElementBase<ValueTypeItemStackLPElementClient<V>> {\n\n    private final IItemStackToValue<V> itemStackToValue;\n    private ItemStack itemStack = ItemStack.EMPTY;\n\n    public ValueTypeItemStackLPElement(IValueType valueType, IItemStackToValue<V> itemStackToValue) {\n        super(valueType);\n        this.itemStackToValue = itemStackToValue;\n    }\n\n    @Override\n    public ValueTypeItemStackLPElementClient<V> constructClient() {\n        return new ValueTypeItemStackLPElementClient<>(this);\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.VALUETYPE;\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.SINGLE_SLOT;\n    }\n\n    @Override\n    public int getItemStackSizeLimit() {\n        return ItemStack.EMPTY.getMaxStackSize();\n    }\n\n    @Override\n    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack) {\n        this.itemStack = itemStack;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return this.itemStackToValue.isNullable() || !this.itemStack.isEmpty();\n    }\n\n    @Override\n    public void activate() {\n        this.itemStack = ItemStack.EMPTY;\n    }\n\n    @Override\n    public void deactivate() {\n\n    }\n\n    @Override\n    public Component validate() {\n        if(!this.itemStackToValue.isNullable() && this.itemStack.isEmpty()) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUTITEM);\n        }\n        return itemStackToValue.validate(itemStack);\n    }\n\n    @Override\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {\n        return true;\n    }\n\n    @Override\n    public IValue getValue() {\n        return this.itemStackToValue.getValue(this.itemStack);\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        this.itemStack = this.itemStackToValue.getValueAsItemStack((V) value);\n    }\n\n    @Override\n    public void setValueInContainer(ContainerLogicProgrammerBase container) {\n        container.getTemporaryInputSlots().setItem(0, this.itemStack);\n    }\n\n    public static interface IItemStackToValue<V extends IValue> {\n\n        public boolean isNullable();\n        public Component validate(ItemStack itemStack);\n        public V getValue(ItemStack itemStack);\n        public ItemStack getValueAsItemStack(V value);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeLPElementBase.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.inventory.ContainerInput;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.item.IValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.item.ValueTypeVariableFacade;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeLPElementBaseClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Consumer;\n\n/**\n * Element for value type.\n * @author rubensworks\n */\npublic abstract class ValueTypeLPElementBase<C extends ValueTypeLPElementBaseClient<?>> implements IValueTypeLogicProgrammerElement<ISubGuiBox, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase, C> {\n\n    private final IValueType<?> valueType;\n    private C client;\n\n    public IValueType<?> getValueType() {\n        return valueType;\n    }\n\n    public ValueTypeLPElementBase(IValueType<?> valueType) {\n        this.valueType = valueType;\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            this.client = constructClient();\n        }\n    }\n\n    @Override\n    public C getClient() {\n        return this.client;\n    }\n\n    public abstract C constructClient();\n\n    @Nullable\n    @Override\n    public <G2 extends Screen, C2 extends AbstractContainerMenu> IGuiInputElementValueType<?, G2, C2, ?> createInnerGuiElement() {\n        return null;\n    }\n\n    @Nullable\n    public IGuiInputElementValueType<?, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase, ?> getInnerGuiElement() {\n        return null;\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder) {\n        getValueType().loadTooltip(tooltipAdder, true, null);\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.VALUETYPE;\n    }\n\n    @Override\n    public String getMatchString() {\n        return getName().getString().toLowerCase();\n    }\n\n    @Override\n    public boolean matchesInput(IValueType<?> valueType) {\n        return false;\n    }\n\n    @Override\n    public boolean matchesOutput(IValueType<?> valueType) {\n        return ValueHelpers.correspondsTo(valueType, valueType);\n    }\n\n    @Override\n    public Component getName() {\n        return Component.translatable(valueType.getTranslationKey());\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.NONE;\n    }\n\n    @Override\n    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack) {\n\n    }\n\n    @Override\n    public boolean isFor(IVariableFacade variableFacade) {\n        if (variableFacade instanceof IValueTypeVariableFacade) {\n            IValueTypeVariableFacade valueTypeFacade = (IValueTypeVariableFacade) variableFacade;\n            if (valueTypeFacade.isValid()) {\n                return getValueType() == valueTypeFacade.getValueType();\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return true;\n    }\n\n    @Override\n    public ItemStack writeElement(Player player, ItemStack itemStack) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        return registry.writeVariableFacadeItem(!player.level().isClientSide(), itemStack, ValueTypes.REGISTRY,\n                new ValueTypeVariableFacadeFactory(getValueType(), getValue()), player.level(), player, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get().defaultBlockState());\n    }\n\n    @Override\n    public void loadElement(IVariableFacade variableFacade) {\n        if (variableFacade instanceof IValueTypeVariableFacade valueTypeVariableFacade) {\n            setValue(valueTypeVariableFacade.getValue());\n        }\n    }\n\n    @Override\n    public boolean canCurrentlyReadFromOtherItem() {\n        return true;\n    }\n\n    @Override\n    public void activate() {\n\n    }\n\n    @Override\n    public void deactivate() {\n\n    }\n\n    @Override\n    public int getColor() {\n        return valueType.getDisplayColor();\n    }\n\n    @Override\n    public String getSymbol() {\n        return IModHelpers.get().getL10NHelpers().localize(valueType.getTranslationKey());\n    }\n\n    @Override\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {\n        return itemStack.getItem() == RegistryEntries.ITEM_VARIABLE.get();\n    }\n\n    @Override\n    public boolean slotClick(int slotId, Slot slot, int mouseButton, ContainerInput clickType, Player player) {\n        return false;\n    }\n\n    @Override\n    public int getItemStackSizeLimit() {\n        return 1;\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        if (getInnerGuiElement() != null) {\n            getInnerGuiElement().setValue(value);\n        }\n    }\n\n    @Override\n    public void setValueInContainer(ContainerLogicProgrammerBase container) {\n\n    }\n\n    public static class ValueTypeVariableFacadeFactory implements IVariableFacadeHandlerRegistry.IVariableFacadeFactory<IValueTypeVariableFacade> {\n\n        private final IValueType valueType;\n        private final IValue value;\n\n        public ValueTypeVariableFacadeFactory(IValueType valueType, IValue value) {\n            this.valueType = valueType;\n            this.value = value;\n        }\n\n        public ValueTypeVariableFacadeFactory(IValue value) {\n            this(value.getType(), value);\n        }\n\n        @Override\n        public IValueTypeVariableFacade create(boolean generateId) {\n            return new ValueTypeVariableFacade(generateId, valueType, value);\n        }\n\n        @Override\n        public IValueTypeVariableFacade create(int id) {\n            return new ValueTypeVariableFacade(id, valueType, value);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeLPElementType.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.List;\n\n/**\n * Value type element type.\n * @author rubensworks\n */\npublic class ValueTypeLPElementType implements ILogicProgrammerElementType<IValueTypeLogicProgrammerElement> {\n\n    @Override\n    public IValueTypeLogicProgrammerElement getByName(Identifier name) {\n        return ValueTypes.REGISTRY.getValueType(name).createLogicProgrammerElement();\n    }\n\n    @Override\n    public Identifier getName(IValueTypeLogicProgrammerElement element) {\n        return element.getValueType().getUniqueName();\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"valuetype\");\n    }\n\n    @Override\n    public List<IValueTypeLogicProgrammerElement> createElements() {\n        ImmutableList.Builder<IValueTypeLogicProgrammerElement> builder = ImmutableList.builder();\n        for(IValueType valueType : ValueTypes.REGISTRY.getValueTypes()) {\n            IValueTypeLogicProgrammerElement valueTypeLPElement = valueType.createLogicProgrammerElement();\n            if(valueTypeLPElement != null) {\n                builder.add(valueTypeLPElement);\n            }\n        }\n        return builder.build();\n    }\n\n    /**\n     * @return All possible value types in this element type.\n     */\n    public List<IValueType<?>> getValueTypes() {\n        ImmutableList.Builder<IValueType<?>> builder = ImmutableList.builder();\n        for(IValueType valueType : ValueTypes.REGISTRY.getValueTypes()) {\n            if(!valueType.isCategory() && valueType.createLogicProgrammerElement() != null) {\n                builder.add(valueType);\n            }\n        }\n        return builder.build();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeListLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeListLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeListValueChangedPacket;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Element for the list value type.\n * @author rubensworks\n */\npublic class ValueTypeListLPElement extends ValueTypeLPElementBase<ValueTypeListLPElementClient> {\n\n    private IValueType listValueType;\n    private Map<Integer, IValueTypeLogicProgrammerElement> subElements;\n    private int length = 0;\n    private int activeElement = -1;\n\n    private ValueTypeList.ValueList serverValue = null;\n\n    public ValueTypeListLPElement() {\n        super(ValueTypes.LIST);\n    }\n\n    public void setServerValue(ValueTypeList.ValueList serverValue) {\n        this.serverValue = serverValue;\n    }\n\n    @Override\n    public ValueTypeListLPElementClient constructClient() {\n        return new ValueTypeListLPElementClient(this);\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.VALUETYPE;\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.NONE_CANVAS_WIDE;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return IModHelpers.get().getMinecraftHelpers().isClientSideThread() ? listValueType != null : serverValue != null;\n    }\n\n    protected List<IValue> constructValues() {\n        List<IValue> valueList = Lists.newArrayListWithExpectedSize(this.length);\n        for (Map.Entry<Integer, IValueTypeLogicProgrammerElement> value : this.subElements.entrySet()) {\n            if(value.getValue().validate() == null) {\n                valueList.add(value.getKey(), value.getValue().getValue());\n            } else {\n                valueList.add(value.getKey(), listValueType.getDefault());\n            }\n        }\n        return valueList;\n    }\n\n    @Override\n    public IValue getValue() {\n        return IModHelpers.get().getMinecraftHelpers().isClientSideThread()\n                ? ValueTypeList.ValueList.ofList(listValueType, constructValues()) : serverValue;\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        if (!IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            serverValue = (ValueTypeList.ValueList) value;\n        }\n\n        IValueTypeListProxy list = ((ValueTypeList.ValueList) value).getRawValue();\n        if (!list.isInfinite()) {\n            setListValueType(list.getValueType());\n            try {\n                int length = list.getLength();\n                this.length = length;\n                for (int i = 0; i < length; i++) {\n                    initializeElement(i).setValue(list.get(i));\n                }\n            } catch (EvaluationException e) {\n                // Ignore exceptions\n            }\n        }\n    }\n\n    @Override\n    public void setValueInContainer(ContainerLogicProgrammerBase container) {\n        if (length > 0) {\n            IValueTypeLogicProgrammerElement subElement = setActiveElement(0);\n            int x = RenderPatternCommon.calculateX(ContainerLogicProgrammerBase.BASE_X, ContainerLogicProgrammerBase.MAX_WIDTH, subElement.getRenderPattern());\n            int y = RenderPatternCommon.calculateY(ContainerLogicProgrammerBase.BASE_Y, ContainerLogicProgrammerBase.MAX_HEIGHT, subElement.getRenderPattern());\n            container.setElementInventory(subElement, x, y);\n            container.getTemporaryInputSlots().removeDirtyMarkListener(container);\n            subElement.setValueInContainer(container);\n            container.getTemporaryInputSlots().addDirtyMarkListener(container);\n        }\n    }\n\n    public IValueType getListValueType() {\n        return listValueType;\n    }\n\n    public void setListValueType(IValueType listValueType) {\n        this.listValueType = listValueType;\n        reset();\n    }\n\n    public void reset() {\n        this.subElements = Maps.newHashMap();\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            getClient().reset();\n        }\n        setLength(0);\n    }\n\n    public int getLength() {\n        return length;\n    }\n\n    public void setLength(int length) {\n        this.length = length;\n        setActiveElement(length - 1);\n    }\n\n    public IValueTypeLogicProgrammerElement initializeElement(int index) {\n        IValueTypeLogicProgrammerElement subElement = listValueType.createLogicProgrammerElement();\n        subElement.activate();\n        subElements.put(index, subElement);\n        return subElement;\n    }\n\n    public int getActiveElement() {\n        return activeElement;\n    }\n\n    public IValueTypeLogicProgrammerElement setActiveElement(int index) {\n        activeElement = index;\n        IValueTypeLogicProgrammerElement subElement;\n        if(index >= 0 && !subElements.containsKey(index)) {\n            subElement = initializeElement(index);\n            subElement.activate();\n        } else {\n            subElement = subElements.get(index);\n        }\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            getClient().setActiveElement(activeElement);\n        }\n        return subElement;\n    }\n\n    public void removeElement(int index) {\n        Map<Integer, IValueTypeLogicProgrammerElement> oldSubElements = subElements;\n        subElements = Maps.newHashMap();\n        for(Map.Entry<Integer, IValueTypeLogicProgrammerElement> entry : oldSubElements.entrySet()) {\n            int i = entry.getKey();\n            if(i < index) {\n                subElements.put(i, entry.getValue());\n            } else if(i > index) {\n                subElements.put(i - 1, entry.getValue());\n            }\n        }\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            getClient().removeElement(index);\n        }\n        setLength(length - 1);\n    }\n\n    public Map<Integer, IValueTypeLogicProgrammerElement> getSubElements() {\n        return subElements;\n    }\n\n    @Override\n    public void activate() {\n        reset();\n    }\n\n    @Override\n    public void deactivate() {\n        this.activeElement = -1;\n    }\n\n    @Override\n    public Component validate() {\n        if(!IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            return serverValue == null ? Component.literal(\"\") : null;\n        }\n        if(IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(new LogicProgrammerValueTypeListValueChangedPacket(ValueDeseralizationContext.ofClient(),\n                    listValueType == null ? ValueTypes.LIST.getDefault() : ValueTypeList.ValueList.ofList(listValueType, constructValues())));\n        }\n        if(this.listValueType == null) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUTITEM);\n        }\n        for(Map.Entry<Integer, IValueTypeLogicProgrammerElement> entry : subElements.entrySet()) {\n            Component error = entry.getValue().validate();\n            if(error != null) {\n                return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDLISTELEMENT, entry.getKey(), error);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {\n        return (slotId == 0 && super.isItemValidForSlot(slotId, itemStack)) ||\n                (activeElement >= 0 && subElements.containsKey(activeElement)\n                        && subElements.get(activeElement).isItemValidForSlot(slotId, itemStack));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeOperatorLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntry;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntryListener;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeDropdownList;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeOperatorLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeOperatorValueChangedPacket;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Element for the operator value type.\n * @author rubensworks\n */\npublic class ValueTypeOperatorLPElement extends ValueTypeLPElementBase<ValueTypeOperatorLPElementClient> implements IDropdownEntryListener {\n\n    private IOperator selectedOperator = null;\n    private GuiElementValueTypeDropdownList<IOperator, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> innerGuiElement;\n\n    public ValueTypeOperatorLPElement() {\n        super(ValueTypes.OPERATOR);\n        Set<IDropdownEntry<IOperator>> operatorEntries = Sets.newLinkedHashSet();\n        for (IOperator operator : Operators.REGISTRY.getOperators()) {\n            operatorEntries.add(new OperatorDropdownEntry(operator));\n        }\n        this.innerGuiElement = new GuiElementValueTypeDropdownList<>(getValueType(), getRenderPattern());\n        getInnerGuiElement().setDropdownPossibilities(operatorEntries);\n        getInnerGuiElement().setDropdownEntryListener(this);\n    }\n\n    @Override\n    public ValueTypeOperatorLPElementClient constructClient() {\n        return new ValueTypeOperatorLPElementClient(this);\n    }\n\n    @Override\n    public GuiElementValueTypeDropdownList<IOperator, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> getInnerGuiElement() {\n        return innerGuiElement;\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.VALUETYPE;\n    }\n\n    @Override\n    public Component validate() {\n        if (selectedOperator == null) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, getInnerGuiElement().getInputString());\n        }\n        return null;\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.NONE_CANVAS;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return selectedOperator != null;\n    }\n\n    @Override\n    public IValue getValue() {\n        return ValueTypeOperator.ValueOperator.of(selectedOperator);\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        this.selectedOperator = ((ValueTypeOperator.ValueOperator) value).getRawValue();\n    }\n\n    @Override\n    public void activate() {\n\n    }\n\n    @Override\n    public void onSetDropdownPossiblity(IDropdownEntry dropdownEntry) {\n        OperatorDropdownEntry operatorDropdownEntry = (OperatorDropdownEntry) dropdownEntry;\n        selectedOperator = operatorDropdownEntry == null ? null : operatorDropdownEntry.getValue();\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread() && selectedOperator != null) {\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeOperatorValueChangedPacket(ValueDeseralizationContext.ofClient(),\n                            ValueTypeOperator.ValueOperator.of(selectedOperator)));\n        }\n    }\n\n    public IOperator getSelectedOperator() {\n        return selectedOperator;\n    }\n\n    public void setSelectedOperator(IOperator selectedOperator) {\n        this.selectedOperator = selectedOperator;\n    }\n\n    public static class OperatorDropdownEntry implements IDropdownEntry<IOperator> {\n\n        private final IOperator operator;\n\n        public OperatorDropdownEntry(IOperator operator) {\n            this.operator = operator;\n        }\n\n        @Override\n        public String getMatchString() {\n            return operator.getLocalizedNameFull().getString();\n        }\n\n        @Override\n        public MutableComponent getDisplayString() {\n            return Component.literal(getMatchString());\n        }\n\n        @Override\n        public List<MutableComponent> getTooltip() {\n            return ValueTypeOperator.getSignatureLines(operator, true);\n        }\n\n        @Override\n        public IOperator getValue() {\n            return operator;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeRecipeAdapterLPElementPropertiesSubGui.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.core.Holder;\nimport net.minecraft.core.HolderSet;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.tags.TagKey;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonCheckbox;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonImage;\nimport org.cyclops.cyclopscore.client.gui.image.Images;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElement;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.client.gui.IDropdownEntry;\nimport org.cyclops.integrateddynamics.core.client.gui.WidgetTextFieldDropdown;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Selection panel for the list element value type.\n */\npublic abstract class ValueTypeRecipeAdapterLPElementPropertiesSubGui<E extends IGuiInputElement> extends RenderPattern<E, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> {\n\n    protected final int slotId;\n    protected ButtonCheckbox inputNbt;\n    protected ButtonCheckbox inputTags;\n    protected ButtonCheckbox inputReusable;\n    protected WidgetTextFieldDropdown<Identifier> inputTagsDropdown;\n    protected ButtonImage inputSave;\n\n    public ValueTypeRecipeAdapterLPElementPropertiesSubGui(E element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                           ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container,\n                                                           int slotId) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        this.slotId = slotId;\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        super.init(guiLeft, guiTop);\n\n        this.inputNbt = new ButtonCheckbox(guiLeft + getX() + 2, guiTop + getY() + 2, 20, 10,\n                Component.translatable(L10NValues.GUI_RECIPE_STRICTNBT), (entry) ->  {\n            // Only allow one checkbox to be true at the same time\n            if (this.inputNbt.isChecked()) {\n                this.inputTags.setChecked(false);\n            }\n            saveGuiToState();\n            loadStateToGui();\n        });\n        this.inputReusable = new ButtonCheckbox(guiLeft + getX() + 2, guiTop + getY() + 12, 20, 10,\n                Component.translatable(L10NValues.GUI_RECIPE_REUSABLE), (entry) -> {\n            saveGuiToState();\n            loadStateToGui();\n        });\n        this.inputTags = new ButtonCheckbox(guiLeft + getX() + 2, guiTop + getY() + 22, 20, 10,\n                Component.translatable(L10NValues.GUI_RECIPE_TAGVARIANTS), (entry) -> {\n            // Only allow one checkbox to be true at the same time\n            if (this.inputTags.isChecked()) {\n                this.inputNbt.setChecked(false);\n            }\n            saveGuiToState();\n            loadStateToGui();\n            if (this.inputTags.isChecked()) {\n                this.inputTagsDropdown.setFocused(true);\n            }\n        });\n        this.inputTagsDropdown = new WidgetTextFieldDropdown<>(Minecraft.getInstance().font,\n                guiLeft + getX() + 2, guiTop + getY() + 33,\n                134, 14,\n                Component.translatable(\"gui.cyclopscore.search\"), true,\n                Sets.newHashSet());\n        this.inputTagsDropdown.setDropdownEntryListener((entry) -> saveGuiToState());\n        this.inputTagsDropdown.setMaxLength(64);\n        this.inputTagsDropdown.setDropdownSize(4);\n        this.inputTagsDropdown.setBordered(false);\n        this.inputTagsDropdown.setTextColor(ARGB.opaque(16777215));\n        this.inputTagsDropdown.setCanLoseFocus(true);\n        this.inputSave = new ButtonImage(guiLeft + getX() + 116, guiTop + getY() + 72,\n                Component.translatable(\"gui.integrateddynamics.button.save\"),\n                (button) -> {\n            // If tag checkbox is checked, only allow exiting if a valid tag has been set\n            if (!this.inputTags.isChecked() || this.inputTagsDropdown.getSelectedDropdownPossibility() != null) {\n                returnToMainGui();\n            } else {\n                this.inputTagsDropdown.setFocused(true);\n            }\n                }, Images.OK);\n\n        // Load button states\n        loadStateToGui();\n        // Show dropdown if a tag was already set\n        if (this.inputTags.isChecked()) {\n            this.inputTagsDropdown.setFocused(true);\n        }\n    }\n\n    protected abstract void returnToMainGui();\n\n    public abstract ItemStack getSlotContents();\n\n    public abstract ItemMatchProperties getSlotProperties();\n\n    private Set<IDropdownEntry<Identifier>> getDropdownEntries() {\n        LinkedHashSet<IDropdownEntry<Identifier>> set = Sets.newLinkedHashSet();\n        if (getSlotContents().isEmpty()) {\n            BuiltInRegistries.ITEM.listTagIds()\n                    .forEach(registeredTag -> set.add(new DropdownEntry(registeredTag.location())));\n\n        } else {\n            getSlotContents().getItem().builtInRegistryHolder().tags()\n                    .forEach(registeredTag -> set.add(new DropdownEntry(registeredTag.location())));\n        }\n        return set;\n    }\n\n    public void loadStateToGui() {\n        ItemMatchProperties props = getSlotProperties();\n        this.inputNbt.setChecked(props.isNbt());\n        this.inputTags.setChecked(props.getItemTag() != null);\n        this.inputReusable.setChecked(props.isReusable());\n        this.inputTagsDropdown.setVisible(this.inputTags.isChecked());\n\n        if (this.inputTags.isChecked()) {\n            Set<IDropdownEntry<Identifier>> dropdownEntries = getDropdownEntries();\n            this.inputTagsDropdown.setPossibilities(dropdownEntries);\n            if (props.getItemTag() != null) {\n                this.inputTagsDropdown.selectPossibility(dropdownEntries.stream()\n                        .filter(e -> e.getMatchString().equals(props.getItemTag()))\n                        .findFirst()\n                        .orElse(null));\n            } else {\n                if (!dropdownEntries.isEmpty()) {\n                    this.inputTagsDropdown.selectPossibility(dropdownEntries.iterator().next());\n                } else {\n                    this.inputTagsDropdown.selectPossibility(null);\n                }\n            }\n        } else {\n            this.inputTagsDropdown.setValue(\"\");\n            this.inputTagsDropdown.setPossibilities(Collections.emptySet());\n        }\n    }\n\n    public void saveGuiToState() {\n        boolean nbt = this.inputNbt.isChecked();\n        String tag = this.inputTags.isChecked() ? this.inputTagsDropdown.getValue() : null;\n        getSlotProperties().setNbt(nbt);\n        getSlotProperties().setItemTag(tag);\n        getSlotProperties().setReusable(this.inputReusable.isChecked());\n    }\n\n    @Override\n    protected boolean drawRenderPattern() {\n        return false;\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n\n        drawSlot(guiGraphics, getX() + guiLeft + 116, getY() + guiTop + 2);\n\n        this.inputNbt.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        guiGraphics.text(fontRenderer, IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_RECIPE_STRICTNBT), guiLeft + getX() + 24, guiTop + getY() + 3, ARGB.opaque(0), false);\n        this.inputReusable.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        guiGraphics.text(fontRenderer, IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_RECIPE_REUSABLE), guiLeft + getX() + 24, guiTop + getY() + 13, ARGB.opaque(0), false);\n        this.inputTags.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        guiGraphics.text(fontRenderer, IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_RECIPE_TAGVARIANTS), guiLeft + getX() + 24, guiTop + getY() + 23, ARGB.opaque(0), false);\n        this.inputSave.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        this.inputTagsDropdown.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n\n        if (this.inputTagsDropdown.isFocused()) {\n            int i = this.inputTagsDropdown.getHoveredVisiblePossibility(mouseX, mouseY);\n            if (i >= 0) {\n                IDropdownEntry<Identifier> hoveredPossibility = this.inputTagsDropdown.getVisiblePossibility(i);\n                drawTagsTooltip(guiGraphics, hoveredPossibility, guiLeft, guiTop, mouseX + 10, mouseY - 20, 6, IModHelpers.get().getGuiHelpers().getSlotSize());\n            }\n        }\n    }\n\n    protected void drawTagsTooltip(GuiGraphicsExtractor guiGraphics, IDropdownEntry<Identifier> hoveredPossibility, int guiLeft, int guiTop,\n                                   int mouseX, int mouseY, int columns, int offset) {\n        int x = mouseX - guiLeft;\n        int y = mouseY - guiTop;\n        List<Item> items = BuiltInRegistries.ITEM.get(TagKey.create(Registries.ITEM, hoveredPossibility.getValue()))\n                .stream()\n                .flatMap(HolderSet.ListBacked::stream)\n                .map(Holder::value)\n                .toList();\n\n        // Draw background\n        TooltipRenderUtil.extractTooltipBackground(guiGraphics, x, y, Math.min(items.size(), columns) * offset,\n                ((items.size() % columns == 0 ? 0 : 1) + (items.size() / columns)) * offset, null);\n\n        // Draw item grid\n        guiGraphics.pose().pushMatrix();\n        guiGraphics.pose().translate(0, 0);\n        int passed = 0;\n        for (Item item : items) {\n            guiGraphics.item(new ItemStack(item), x, y);\n            x += offset;\n            if (passed++ % columns == columns - 1) {\n                y += offset;\n                x = mouseX - guiLeft;\n            }\n        }\n        guiGraphics.pose().popMatrix();\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (inputTagsDropdown.isFocused()) {\n            if (inputTagsDropdown.charTyped(evt)) {\n                return true;\n            }\n        }\n        return super.charTyped(evt);\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (inputTagsDropdown.isFocused()) {\n            inputTagsDropdown.keyPressed(evt);\n            return true;\n        }\n        return super.keyPressed(evt);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return inputNbt.mouseClicked(evt, isDoubleClick)\n                || inputReusable.mouseClicked(evt, isDoubleClick)\n                || inputTags.mouseClicked(evt, isDoubleClick)\n                || inputTagsDropdown.mouseClicked(evt, isDoubleClick)\n                || inputSave.mouseClicked(evt, isDoubleClick)\n                || super.mouseClicked(evt, isDoubleClick);\n    }\n\n    public static class DropdownEntry implements IDropdownEntry<Identifier> {\n        private final Identifier tag;\n\n        public DropdownEntry(Identifier tag) {\n            this.tag = tag;\n        }\n\n        @Override\n        public String getMatchString() {\n            return this.tag.toString();\n        }\n\n        @Override\n        public MutableComponent getDisplayString() {\n            return Component.literal(this.tag.toString());\n        }\n\n        @Override\n        public List<MutableComponent> getTooltip() {\n            return Collections.emptyList();\n        }\n\n        @Override\n        public Identifier getValue() {\n            return this.tag;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeRecipeLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.IdentifierException;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.core.HolderSet;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.tags.TagKey;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.ContainerInput;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.commoncapabilities.api.capability.fluidhandler.FluidMatch;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.inventory.slot.SlotExtended;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeRecipeLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\n/**\n * Element for recipes.\n * This is hardcoded to only support items, fluids and energy\n * @author rubensworks\n */\npublic class ValueTypeRecipeLPElement extends ValueTypeLPElementBase<ValueTypeRecipeLPElementClient> {\n\n    public static final int SLOT_OFFSET = 4;\n    public static final int TICK_DELAY = 30;\n\n    private NonNullList<ItemMatchProperties> inputStacks;\n    private ItemStack inputFluid;\n    private String inputFluidAmount = \"0\";\n    private String inputEnergy = \"0\";\n    private NonNullList<ItemStack> outputStacks;\n    private ItemStack outputFluid;\n    private String outputFluidAmount = \"0\";\n    private String outputEnergy = \"0\";\n\n    public NonNullList<ItemMatchProperties> getInputStacks() {\n        return inputStacks;\n    }\n\n    public String getInputFluidAmount() {\n        return inputFluidAmount;\n    }\n\n    public void setInputFluidAmount(String inputFluidAmount) {\n        this.inputFluidAmount = inputFluidAmount;\n    }\n\n    public String getInputEnergy() {\n        return inputEnergy;\n    }\n\n    public void setInputEnergy(String inputEnergy) {\n        this.inputEnergy = inputEnergy;\n    }\n\n    public String getOutputFluidAmount() {\n        return outputFluidAmount;\n    }\n\n    public void setOutputFluidAmount(String outputFluidAmount) {\n        this.outputFluidAmount = outputFluidAmount;\n    }\n\n    public String getOutputEnergy() {\n        return outputEnergy;\n    }\n\n    public void setOutputEnergy(String outputEnergy) {\n        this.outputEnergy = outputEnergy;\n    }\n\n    public ValueTypeRecipeLPElement() {\n        super(ValueTypes.OBJECT_RECIPE);\n    }\n\n    @Override\n    public ValueTypeRecipeLPElementClient constructClient() {\n        return new ValueTypeRecipeLPElementClient(this);\n    }\n\n    @Override\n    public ILogicProgrammerElementType getType() {\n        return LogicProgrammerElementTypes.VALUETYPE;\n    }\n\n    @Override\n    public IConfigRenderPattern getRenderPattern() {\n        return IConfigRenderPattern.RECIPE;\n    }\n\n    @Override\n    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack) {\n        if (inputStacks == null) {\n            return;\n        }\n\n        if (slotId >= 0 && slotId < 9) {\n            ItemStack itemStackOld = inputStacks.get(slotId).getItemStack();\n            if (itemStackOld.getItem() != itemStack.getItem()) {\n                inputStacks.set(slotId, new ItemMatchProperties(itemStack.copy()));\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                    getClient().refreshPropertiesGui(slotId);\n                }\n            }\n        }\n        if (slotId == 9) {\n            inputFluid = itemStack.copy();\n            if (inputFluidAmount.equalsIgnoreCase(\"0\")) {\n                int amount = IModHelpersNeoForge.get().getFluidHelpers().getAmount(Helpers.getFluidStack(inputFluid));\n                inputFluidAmount = Integer.toString(amount);\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                    getClient().refreshInputFluidAmountBox();\n                }\n            }\n        }\n        if (slotId > 9 && slotId < 13) {\n            outputStacks.set(slotId - 10, itemStack.copy());\n        }\n        if (slotId == 13) {\n            outputFluid = itemStack.copy();\n            if (outputFluidAmount.equalsIgnoreCase(\"0\")) {\n                int amount = IModHelpersNeoForge.get().getFluidHelpers().getAmount(Helpers.getFluidStack(outputFluid));\n                outputFluidAmount = Integer.toString(amount);\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                    getClient().refreshOutputFluidAmountBox();\n                }\n            }\n        }\n    }\n\n    public void sendSlotPropertiesToServer(int slotId, ItemMatchProperties props) {\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                new LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket(\n                        slotId, props.isNbt(), props.getItemTag() == null ? \"\" : props.getItemTag(), props.getTagQuantity(), props.isReusable()));\n    }\n\n    // Used by ID-Compat for JEI recipe transfer handler\n    public boolean isValidForRecipeGrid(List<ItemMatchProperties> itemInputs, List<FluidStack> fluidInputs,\n                                        List<ItemStack> itemOutputs, List<FluidStack> fluidOutputs) {\n        return itemInputs.size() <= 9 && itemOutputs.size() <= 3\n                && fluidInputs.size() <= 1 && fluidOutputs.size() <= 1;\n    }\n\n    protected void putItemPropertiesInContainer(ContainerLogicProgrammerBase container, int slot, ItemMatchProperties props) {\n        putStackInContainer(container, slot, props.getItemStack());\n        getInputStacks().set(slot, props);\n    }\n\n    protected void putStackInContainer(ContainerLogicProgrammerBase container, int slot, ItemStack itemStack) {\n        // Offset: Player inventory, recipe grid slots\n        container.setItem(container.getItems().size() - (36 + 14) + slot, 0, itemStack);\n    }\n\n    // Used by ID-Compat for JEI recipe transfer handler\n    public void setRecipeGrid(ContainerLogicProgrammerBase container,\n                              List<ItemMatchProperties> itemInputs, List<FluidStack> fluidInputs,\n                              List<ItemStack> itemOutputs, List<FluidStack> fluidOutputs) {\n        int slot = 0;\n\n        // Fill input item slots\n        for (ItemMatchProperties itemInput : itemInputs) {\n            putItemPropertiesInContainer(container, slot, itemInput);\n            slot++;\n        }\n        while (slot < 9) {\n            putItemPropertiesInContainer(container, slot, new ItemMatchProperties(ItemStack.EMPTY));\n            slot++;\n        }\n\n        // Fill input fluid slot\n        slot = 9;\n        FluidStack fluidStackInput = FluidStack.EMPTY;\n        if (fluidInputs.size() > 0) {\n            fluidStackInput = fluidInputs.get(0);\n        }\n        putStackInContainer(container, slot, fluidStackInput.isEmpty() ? ItemStack.EMPTY : getFluidBucket(fluidStackInput));\n        inputFluidAmount = String.valueOf(IModHelpersNeoForge.get().getFluidHelpers().getAmount(fluidStackInput));\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            getClient().refreshInputFluidAmountBox();\n        }\n\n        // Fill input output slots\n        slot = 10;\n        for (ItemStack itemOutput : itemOutputs) {\n            putStackInContainer(container, slot, itemOutput);\n            slot++;\n        }\n        while (slot < 13) {\n            putStackInContainer(container, slot, ItemStack.EMPTY);\n            slot++;\n        }\n\n        // Fill output fluid slot\n        slot = 13;\n        FluidStack fluidStackOutput = FluidStack.EMPTY;\n        if (fluidOutputs.size() > 0) {\n            fluidStackOutput = fluidOutputs.get(0);\n        }\n        putStackInContainer(container, slot, fluidStackOutput.isEmpty() ? ItemStack.EMPTY : getFluidBucket(fluidStackOutput));\n        outputFluidAmount = String.valueOf(IModHelpersNeoForge.get().getFluidHelpers().getAmount(fluidStackOutput));\n        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            getClient().refreshOutputFluidAmountBox();\n        }\n    }\n\n    public static ItemStack getFluidBucket(FluidStack fluidStack) {\n        ItemStack itemStack = new ItemStack(Items.BUCKET);\n        ItemAccess itemAccess = ItemAccess.forStack(itemStack);\n        ResourceHandler<FluidResource> fluidHandler = itemAccess.getCapability(Capabilities.Fluid.ITEM);\n        try (var tx = Transaction.openRoot()) {\n            fluidHandler.insert(FluidResource.of(fluidStack.getFluid()), IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume(), tx);\n            tx.commit();\n        }\n        return itemAccess.getResource().toStack(itemAccess.getAmount());\n    }\n\n    protected boolean isInputValid() {\n        return inputStacks.stream().anyMatch(ItemMatchProperties::isValid)\n                || !inputFluid.isEmpty() || !inputFluidAmount.equalsIgnoreCase(\"0\")\n                || !inputEnergy.equalsIgnoreCase(\"0\");\n    }\n\n    protected boolean isOutputValid() {\n        return outputStacks.stream().anyMatch(stack -> !stack.isEmpty())\n                || !outputFluid.isEmpty() || !outputFluidAmount.equalsIgnoreCase(\"0\")\n                || !outputEnergy.equalsIgnoreCase(\"0\");\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return isInputValid() == isOutputValid(); // Not &&, because we also allow fully blank recipes\n    }\n\n    @Override\n    public void activate() {\n        inputStacks = NonNullList.withSize(9, new ItemMatchProperties(ItemStack.EMPTY));\n        for (int i = 0; i < 9; i++) {\n            inputStacks.set(i, new ItemMatchProperties(ItemStack.EMPTY));\n        }\n        inputFluid = ItemStack.EMPTY;\n        inputFluidAmount = \"0\";\n        inputEnergy = \"0\";\n        outputStacks = NonNullList.withSize(3, ItemStack.EMPTY);\n        outputFluid = ItemStack.EMPTY;\n        outputFluidAmount = \"0\";\n        outputEnergy = \"0\";\n    }\n\n    @Override\n    public void deactivate() {\n\n    }\n\n    @Override\n    public Component validate() {\n        if (!inputFluid.isEmpty() && Helpers.getFluidStack(inputFluid).isEmpty()) {\n            return Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);\n        }\n        if (!outputFluid.isEmpty() && Helpers.getFluidStack(outputFluid).isEmpty()) {\n            return Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);\n        }\n        try {\n            Integer.parseInt(inputFluidAmount);\n        } catch (NumberFormatException e) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputFluidAmount);\n        }\n        try {\n            Integer.parseInt(outputFluidAmount);\n        } catch (NumberFormatException e) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, outputFluidAmount);\n        }\n        try {\n            Long.parseLong(inputEnergy);\n        } catch (NumberFormatException e) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputEnergy);\n        }\n        try {\n            Long.parseLong(outputEnergy);\n        } catch (NumberFormatException e) {\n            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, outputEnergy);\n        }\n        // Validate input item tag strings if they are defined\n        for (ItemMatchProperties inputStack : inputStacks) {\n            if (inputStack.getItemTag() != null) {\n                try {\n                    Identifier.parse(inputStack.getItemTag());\n                } catch (IdentifierException e) {\n                    return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputStack.getItemTag());\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {\n        return true;\n    }\n\n    @Override\n    public boolean slotClick(int slotId, Slot slot, int mouseButton, ContainerInput clickType, Player player) {\n        return slotClickCommon(slotId, slot, mouseButton, clickType, player, getInputStacks(), 9, (i) -> {\n            if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                getClient().setPropertySubGui(i);\n            }\n        }, (i) -> {\n            if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                getClient().refreshPropertiesGui(i);\n            }\n        }) || super.slotClick(slotId, slot, mouseButton, clickType, player);\n    }\n\n    public static boolean slotClickCommon(int slotId, Slot slot, int mouseButton, ContainerInput clickType, Player player,\n                                          List<ItemMatchProperties> inputStacks, int propertySlotCount,\n                                          Consumer<Integer> setPropertySubGui,\n                                          Consumer<Integer> refreshPropertiesGui) {\n        if (slotId >= SLOT_OFFSET && slotId < propertySlotCount + SLOT_OFFSET) {\n            if (clickType == ContainerInput.QUICK_MOVE && mouseButton == 0) {\n                if (player.level().isClientSide()) {\n                    int id = slotId - SLOT_OFFSET;\n                    setPropertySubGui.accept(id);\n                }\n                return true;\n            } else {\n                // Similar logic as ContainerExtended.adjustPhantomSlot\n                ItemMatchProperties props = inputStacks.get(slotId - SLOT_OFFSET);\n                int quantityCurrent = props.getTagQuantity();\n                int quantityNew;\n                if (clickType == ContainerInput.QUICK_MOVE) {\n                    quantityNew = mouseButton == 0 ? (quantityCurrent + 1) / 2 : quantityCurrent * 2;\n                } else {\n                    quantityNew = mouseButton == 0 ? quantityCurrent - 1 : quantityCurrent + 1;\n                }\n\n                if (quantityNew > slot.getMaxStackSize()) {\n                    quantityNew = slot.getMaxStackSize();\n                }\n\n                props.setTagQuantity(quantityNew);\n                if (!props.getItemStack().isEmpty()) {\n                    props.getItemStack().setCount(quantityNew);\n                }\n\n                if (quantityNew <= 0) {\n                    props.setItemTag(null);\n                    props.setTagQuantity(1);\n                    if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                        refreshPropertiesGui.accept(slotId - SLOT_OFFSET);\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    @Override\n    public Slot createSlot(Container temporaryInputSlots, int slotId, int x, int y) {\n        SlotExtended slot = new SlotExtended(temporaryInputSlots, slotId, x, y) {\n            @Override\n            public boolean mayPlace(ItemStack itemStack) {\n                return ValueTypeRecipeLPElement.this.isItemValidForSlot(slotId, itemStack);\n            }\n\n            @Override\n            public ItemStack getItem() {\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread() && slotId < 9) {\n                    return getRotatingItemFromTag(getInputStacks().get(slotId))\n                            .orElseGet(super::getItem);\n                }\n                return super.getItem();\n            }\n        };\n        slot.setPhantom(true);\n        return slot;\n    }\n\n    public static Optional<ItemStack> getRotatingItemFromTag(ItemMatchProperties props) {\n        String tagName = props.getItemTag();\n        if (tagName != null) {\n            try {\n                Optional<HolderSet.Named<Item>> tag = BuiltInRegistries.ITEM.get(TagKey.create(Registries.ITEM, Identifier.parse(tagName)));\n                if (!tag.isEmpty()) {\n                    List<Item> items = tag.stream()\n                            .flatMap(holders -> holders.stream())\n                            .map(holder -> holder.value()).toList();\n                    int tick = ((int) Minecraft.getInstance().level.getGameTime()) / TICK_DELAY;\n                    Item item = items.get(tick % items.size());\n                    return Optional.of(new ItemStack(item, props.getTagQuantity()));\n                }\n            } catch (IdentifierException e) {\n                // Ignore invalid tags\n            }\n        }\n        return Optional.empty();\n    }\n\n    @Override\n    public int getItemStackSizeLimit() {\n        return 64;\n    }\n\n    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getInputs(List<ItemMatchProperties> itemStacks,\n                                                                                                      ItemStack fluid, int fluidAmount,\n                                                                                                      long energy) {\n        // Cut of itemStacks list until last non-empty stack\n        int lastNonEmpty = 0;\n        for (int i = 0; i < itemStacks.size(); i++) {\n            if (itemStacks.get(i).isValid()) {\n                lastNonEmpty = i + 1;\n            }\n        }\n        itemStacks = itemStacks.subList(0, lastNonEmpty);\n\n        // Override fluid amount\n        FluidStack fluidStack = Helpers.getFluidStack(fluid);\n        if (!fluidStack.isEmpty()) {\n            fluidStack.setAmount(fluidAmount);\n        }\n\n        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();\n        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> items = itemStacks.stream()\n                .map(ItemMatchProperties::createPrototypedIngredient)\n                .collect(Collectors.toList());\n        List<IPrototypedIngredientAlternatives<FluidStack, Integer>> fluids = !fluidStack.isEmpty()\n                ? Collections.singletonList(new PrototypedIngredientAlternativesList<>(\n                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.FLUIDSTACK, fluidStack, FluidMatch.FLUID | FluidMatch.DATA))))\n                : Collections.emptyList();\n        List<IPrototypedIngredientAlternatives<Long, Boolean>> energies = energy > 0 ?\n                Collections.singletonList(new PrototypedIngredientAlternativesList<>(\n                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ENERGY, energy, false))))\n                : Collections.emptyList();\n        if (!items.isEmpty()) {\n            inputs.put(IngredientComponent.ITEMSTACK, (List) items);\n        }\n        if (!fluids.isEmpty()) {\n            inputs.put(IngredientComponent.FLUIDSTACK, (List) fluids);\n        }\n        if (!energies.isEmpty()) {\n            inputs.put(IngredientComponent.ENERGY, (List) energies);\n        }\n\n        return inputs;\n    }\n\n    public static Map<IngredientComponent<?, ?>, List<Boolean>> getInputsReusable(List<ItemMatchProperties> itemStacks) {\n        Map<IngredientComponent<?, ?>, List<Boolean>> inputs = Maps.newIdentityHashMap();\n\n        List<Boolean> items = itemStacks.stream()\n                .map(ItemMatchProperties::isReusable)\n                .collect(Collectors.toList());\n        if (!items.isEmpty()) {\n            inputs.put(IngredientComponent.ITEMSTACK, (List) items);\n        }\n\n        return inputs;\n    }\n\n    protected Map<IngredientComponent<?, ?>, List<?>> getOutputs(List<ItemStack> itemStacksIn,\n                                                                 ItemStack fluid, int fluidAmount,\n                                                                 long energy) {\n        // Cut of itemStacks list until last non-empty stack\n        List<ItemStack> itemStacks = Lists.newArrayList();\n        for (int i = 0; i < itemStacksIn.size(); i++) {\n            if (!itemStacksIn.get(i).isEmpty()) {\n                itemStacks.add(itemStacksIn.get(i));\n            }\n        }\n\n        // Override fluid amount\n        FluidStack fluidStack = Helpers.getFluidStack(fluid);\n        if (!fluidStack.isEmpty()) {\n            fluidStack.setAmount(fluidAmount);\n        }\n\n        Map<IngredientComponent<?, ?>, List<?>> outputs = Maps.newIdentityHashMap();\n        if (!itemStacks.isEmpty()) {\n            outputs.put(IngredientComponent.ITEMSTACK, itemStacks);\n        }\n        if (!fluidStack.isEmpty()) {\n            outputs.put(IngredientComponent.FLUIDSTACK, Collections.singletonList(fluidStack));\n        }\n        if (energy > 0) {\n            outputs.put(IngredientComponent.ENERGY, Collections.singletonList(energy));\n        }\n\n        return outputs;\n    }\n\n    @Override\n    public IValue getValue() {\n        if (!isInputValid() && !isOutputValid()) {\n            return ValueObjectTypeRecipe.ValueRecipe.of(null);\n        }\n        return ValueObjectTypeRecipe.ValueRecipe.of(\n                new RecipeDefinition(getInputs(this.inputStacks, this.inputFluid,\n                        Integer.parseInt(this.inputFluidAmount), Long.parseLong(this.inputEnergy)),\n                getInputsReusable(this.inputStacks),\n                new MixedIngredients(getOutputs(this.outputStacks, this.outputFluid,\n                        Integer.parseInt(this.outputFluidAmount), Long.parseLong(this.outputEnergy)))));\n    }\n\n    @Override\n    public void setValue(IValue value) {\n        ValueObjectTypeRecipe.ValueRecipe valueRecipe = (ValueObjectTypeRecipe.ValueRecipe) value;\n        valueRecipe.getRawValue().ifPresent(recipe -> {\n            loadInputStacks(recipe);\n            loadInputFluid(recipe);\n            loadInputEnergy(recipe);\n\n            loadOutputStacks(recipe);\n            loadOutputFluid(recipe);\n            loadOutputEnergy(recipe);\n        });\n    }\n\n    private void loadInputStacks(IRecipeDefinition recipe) {\n        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> listAlternatives = recipe.getInputs(IngredientComponent.ITEMSTACK);\n        for (int i = 0; i < listAlternatives.size(); i++) {\n            IPrototypedIngredientAlternatives<ItemStack, Integer> prototypes = listAlternatives.get(i);\n            boolean reusable = recipe.isInputReusable(IngredientComponent.ITEMSTACK, i);\n            ItemMatchProperties itemMatchProperties = ItemMatchProperties.fromPrototypedIngredient(prototypes, reusable);\n            this.inputStacks.set(i, itemMatchProperties);\n        }\n    }\n\n    private <T, M> Optional<T> loadFirstInput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {\n        List<IPrototypedIngredientAlternatives<T, M>> listAlternatives = recipe.getInputs(ingredientComponent);\n        if (listAlternatives.size() > 0) {\n            IPrototypedIngredientAlternatives<T, M> prototypes = listAlternatives.get(0);\n            if (prototypes.getAlternatives().size() > 0) {\n                IPrototypedIngredient<T, M> prototype = prototypes.getAlternatives().stream().findFirst().get();\n                return Optional.of(prototype.getPrototype());\n            }\n        }\n        return Optional.empty();\n    }\n\n    private void loadInputFluid(IRecipeDefinition recipe) {\n        loadFirstInput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {\n            this.inputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);\n            this.inputFluidAmount = Integer.toString(fluidStack.getAmount());\n        });\n    }\n\n    private void loadInputEnergy(IRecipeDefinition recipe) {\n        loadFirstInput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.inputEnergy = Long.toString(energy));\n    }\n\n    private void loadOutputStacks(IRecipeDefinition recipe) {\n        List<ItemStack> instances = recipe.getOutput().getInstances(IngredientComponent.ITEMSTACK);\n        if (instances.size() > 0) {\n            outputStacks.set(0, instances.get(0));\n        }\n        if (instances.size() > 1) {\n            outputStacks.set(1, instances.get(1));\n        }\n        if (instances.size() > 2) {\n            outputStacks.set(2, instances.get(2));\n        }\n    }\n\n    private <T, M> Optional<T> loadFirstOutput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {\n        List<T> instances = recipe.getOutput().getInstances(ingredientComponent);\n        if (instances.size() > 0) {\n            return Optional.of(instances.get(0));\n        }\n        return Optional.empty();\n    }\n\n    private void loadOutputFluid(IRecipeDefinition recipe) {\n        loadFirstOutput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {\n            this.outputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);\n            this.outputFluidAmount = Integer.toString(fluidStack.getAmount());\n        });\n    }\n\n    private void loadOutputEnergy(IRecipeDefinition recipe) {\n        loadFirstOutput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.outputEnergy = Long.toString(energy));\n    }\n\n    @Override\n    public void setValueInContainer(ContainerLogicProgrammerBase container) {\n        Container slots = container.getTemporaryInputSlots();\n\n        // Input slots\n        for (int i = 0; i < this.inputStacks.size(); i++) {\n            ItemMatchProperties entry = this.inputStacks.get(i);\n            slots.setItem(i, entry.getItemStack());\n        }\n        slots.setItem(9, this.inputFluid);\n\n        // Output slots\n        for (int i = 0; i < this.outputStacks.size(); i++) {\n            slots.setItem(10 + i, this.outputStacks.get(i));\n            // No need to set slot type, as this can't be changed for output stacks\n        }\n        slots.setItem(13, this.outputFluid);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeStringLPElement.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer;\n\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeString;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeStringLPElementClient;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\nimport javax.annotation.Nullable;\n\n/**\n * Element for value types that can be read from and written to strings.\n * @author rubensworks\n */\npublic class ValueTypeStringLPElement extends ValueTypeLPElementBase<ValueTypeStringLPElementClient> {\n\n    private GuiElementValueTypeString<ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> innerGuiElement;\n\n    public ValueTypeStringLPElement(IValueType valueType) {\n        super(valueType);\n        this.innerGuiElement = createInnerGuiElement();\n    }\n\n    @Override\n    public ValueTypeStringLPElementClient constructClient() {\n        return new ValueTypeStringLPElementClient(this);\n    }\n\n    @Nullable\n    @Override\n    public <G2 extends Screen, C2 extends AbstractContainerMenu> GuiElementValueTypeString<G2, C2> createInnerGuiElement() {\n        return new GuiElementValueTypeString<>(getValueType(), getRenderPattern());\n    }\n\n    @Override\n    public GuiElementValueTypeString<ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> getInnerGuiElement() {\n        return innerGuiElement;\n    }\n\n    @Override\n    public boolean canWriteElementPre() {\n        return getInnerGuiElement().getInputString() != null;\n    }\n\n    @Override\n    public boolean canCurrentlyReadFromOtherItem() {\n        return this.getInnerGuiElement().getInputString() == null || this.getInnerGuiElement().getInputString().equals(getInnerGuiElement().getDefaultInputString());\n    }\n\n    @Override\n    public void activate() {\n        getInnerGuiElement().setInputString(getInnerGuiElement().getDefaultInputString());\n    }\n\n    @Override\n    public void deactivate() {\n        getInnerGuiElement().setInputString(null);\n    }\n\n    @Override\n    public Component validate() {\n        try {\n            ValueHelpers.parseString(getInnerGuiElement().getValueType(), getInnerGuiElement().getInputString());\n        } catch (EvaluationException e) {\n            return e.getErrorMessage();\n        }\n        return null;\n    }\n\n    @Override\n    public IValue getValue() {\n       return getInnerGuiElement().getValue();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/OperatorLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementClient;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.OperatorLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class OperatorLPElementClient<T extends OperatorLPElement>\n        implements ILogicProgrammerElementClient<RenderPattern, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> {\n\n    private final T element;\n\n    public OperatorLPElementClient(T element) {\n        this.element = element;\n    }\n\n    public T getElement() {\n        return element;\n    }\n\n    @Override\n    public void setValueInGui(RenderPattern subGui) {\n        this.element.setValueInContainer((ContainerLogicProgrammerBase) subGui.getContainer());\n    }\n\n    @Override\n    public boolean isFocused(RenderPattern subGui) {\n        return this.element.isFocused();\n    }\n\n    @Override\n    public void setFocused(RenderPattern subGui, boolean focused) {\n        this.element.setFocused(focused);\n    }\n\n    @Override\n    public RenderPattern createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                      ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return new OperatorLPElementRenderPattern(this.element, baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/OperatorLPElementRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.world.Container;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.OperatorLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class OperatorLPElementRenderPattern extends RenderPattern<OperatorLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n        implements IRenderPatternValueTypeTooltip {\n\n    private boolean renderTooltip = true;\n\n    public OperatorLPElementRenderPattern(OperatorLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                          ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n        IConfigRenderPattern configRenderPattern = element.getRenderPattern();\n        IOperator operator = element.getOperator();\n\n        // Input type tooltips\n        IValueType[] valueTypes = operator.getInputTypes();\n        for (int i = 0; i < valueTypes.length; i++) {\n            IValueType valueType = valueTypes[i];\n            Container temporaryInputSlots = container.getTemporaryInputSlots();\n            if (temporaryInputSlots.getItem(i).isEmpty()) {\n                Pair<Integer, Integer> slotPosition = configRenderPattern.getSlotPositions()[i];\n                if (gui.isHovering(getX() + slotPosition.getLeft(), getY() + slotPosition.getRight(),\n                        ContainerScreenLogicProgrammerBase.BOX_HEIGHT, ContainerScreenLogicProgrammerBase.BOX_HEIGHT, mouseX, mouseY)) {\n                    gui.drawTooltip(getValueTypeTooltip(valueType), guiGraphics, mouseX, mouseY);\n                }\n            }\n        }\n\n        // Output type tooltip\n        this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, operator.getOutputType());\n    }\n\n    @Override\n    public boolean isRenderTooltip() {\n        return this.renderTooltip;\n    }\n\n    @Override\n    public void setRenderTooltip(boolean renderTooltip) {\n        this.renderTooltip = renderTooltip;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/RenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElement;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;\nimport org.cyclops.integrateddynamics.core.client.gui.subgui.SubGuiBox;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.RenderPatternCommon;\n\n/**\n * Sub gui for rendering logic programmer elements.\n * @author rubensworks\n */\npublic class RenderPattern<E extends IGuiInputElement, G extends Screen, C extends AbstractContainerMenu> extends SubGuiBox implements ISubGuiBox {\n\n    protected final E element;\n    private final int x, y;\n    protected final G gui;\n    protected final C container;\n    protected Identifier texture = Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"textures/gui/logic_programmer.png\");\n\n    public E getElement() {\n        return element;\n    }\n\n    public RenderPattern(E element, int baseX, int baseY, int maxWidth, int maxHeight,\n                         G gui, C container) {\n        super(SubGuiBox.Box.LIGHT);\n        this.element = element;\n        IConfigRenderPattern configRenderPattern = element.getRenderPattern();\n        this.x = RenderPatternCommon.calculateX(baseX, maxWidth, configRenderPattern);\n        this.y = RenderPatternCommon.calculateY(baseY, maxHeight, configRenderPattern);\n        this.gui = gui;\n        this.container = container;\n    }\n\n    public C getContainer() {\n        return container;\n    }\n\n    protected void drawSlot(GuiGraphicsExtractor guiGraphics, int x, int y) {\n        guiGraphics.blit(RenderPipelines.GUI_TEXTURED, SubGuiBox.TEXTURE, x, y, 19, 0, 18, 18, 256, 256);\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        super.init(guiLeft, guiTop);\n    }\n\n    @Override\n    public void tick() {\n\n    }\n\n    protected boolean drawRenderPattern() {\n        return true;\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n        if (drawRenderPattern()) {\n            IConfigRenderPattern configRenderPattern = element.getRenderPattern();\n\n            int baseX = getX() + guiLeft;\n            int baseY = getY() + guiTop;\n\n            for (Pair<Integer, Integer> slot : configRenderPattern.getSlotPositions()) {\n                drawSlot(guiGraphics, baseX + slot.getLeft(), baseY + slot.getRight());\n            }\n\n            if (configRenderPattern.getSymbolPosition() != null) {\n                IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, fontRenderer, element.getSymbol(),\n                        baseX + configRenderPattern.getSymbolPosition().getLeft(),\n                        baseY + configRenderPattern.getSymbolPosition().getRight() + 8,\n                        0, 1, 0, false, Font.DisplayMode.NORMAL);\n            }\n        }\n    }\n\n    @Override\n    public int getX() {\n        return this.x;\n    }\n\n    @Override\n    public int getY() {\n        return this.y;\n    }\n\n    @Override\n    public int getWidth() {\n        return element.getRenderPattern().getWidth();\n    }\n\n    @Override\n    public int getHeight() {\n        return element.getRenderPattern().getHeight();\n    }\n\n    public void sendValueToServer() {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeBooleanLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeBooleanRenderPattern;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeBooleanLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeBooleanLPElementClient extends ValueTypeLPElementBaseClient<ValueTypeBooleanLPElement> {\n\n    public ValueTypeBooleanLPElementClient(ValueTypeBooleanLPElement element) {\n        super(element);\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return new GuiElementValueTypeBooleanRenderPattern<RenderPattern, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>(this.getElement().getInnerGuiElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeIngredientsLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonArrow;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.component.input.IInputListener;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetArrowedListField;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.ingredient.IngredientComponentHandlers;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeIngredientsLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerSetElementInventory;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeIngredientsLPElementClient extends ValueTypeLPElementBaseClient<ValueTypeIngredientsLPElement> {\n\n    private ValueTypeIngredientsLPElementClient.MasterSubGuiRenderPattern masterGui;\n    private Map<IngredientComponent, Map<Integer, RenderPattern>> subElementGuis = Maps.newHashMap();\n\n    public ValueTypeIngredientsLPElementClient(ValueTypeIngredientsLPElement element) {\n        super(element);\n    }\n\n    @Override\n    public void setValueInGui(ISubGuiBox subGui) {\n        if (!getElement().getSubElements().get(getElement().getCurrentType()).isEmpty()) {\n            getElement().setActiveElement(0);\n        }\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return masterGui = new ValueTypeIngredientsLPElementClient.MasterSubGuiRenderPattern(this.getElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    public void setActiveElement(int activeElement) {\n        if (masterGui != null) {\n            masterGui.setActiveElement(activeElement);\n            masterGui.container.onDirty();\n        }\n    }\n\n    public void removeElement(int index) {\n        IngredientComponent currentType = getElement().getCurrentType();\n        Map<Integer, RenderPattern> oldSubElementGuis = subElementGuis.get(currentType);\n        subElementGuis.put(currentType, Maps.newHashMap());\n        for (Map.Entry<Integer, RenderPattern> entry : oldSubElementGuis.entrySet()) {\n            int i = entry.getKey();\n            if (i < index) {\n                subElementGuis.get(currentType).put(i, entry.getValue());\n            } else if (i > index) {\n                subElementGuis.get(currentType).put(i - 1, entry.getValue());\n            }\n        }\n    }\n\n    public void activate() {\n        for (IngredientComponent recipeComponent : IngredientComponentHandlers.REGISTRY.getComponents()) {\n            subElementGuis.put(recipeComponent, Maps.newHashMap());\n        }\n    }\n\n    /**\n     * Sub gui that holds the list element value type panel and the panel for browsing through the elements.\n     */\n    protected static class MasterSubGuiRenderPattern extends RenderPattern<ValueTypeIngredientsLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n            implements IRenderPatternValueTypeTooltip {\n\n        private final int baseX;\n        private final int baseY;\n        private final int maxWidth;\n        private final int maxHeight;\n        private final ContainerScreenLogicProgrammerBase gui;\n        private final ContainerLogicProgrammerBase container;\n\n        protected ListElementSubGui elementSubGui = null;\n        protected int lastGuiLeft;\n        protected int lastGuiTop;\n        private boolean renderTooltip = true;\n\n        public MasterSubGuiRenderPattern(ValueTypeIngredientsLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                         ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n            subGuiHolder.addSubGui(new SelectionSubGui(element, baseX, baseY, maxWidth, maxHeight, gui, container));\n            this.baseX = baseX;\n            this.baseY = baseY;\n            this.maxWidth = maxWidth;\n            this.maxHeight = maxHeight;\n            this.gui = gui;\n            this.container = container;\n        }\n\n        public void setActiveElement(int index) {\n            if (elementSubGui != null) {\n                subGuiHolder.removeSubGui(elementSubGui);\n                ((ContainerLogicProgrammerBase) gui.getMenu()).setElementInventory(null, 0, 0);\n            }\n            if (index >= 0) {\n                subGuiHolder.addSubGui(elementSubGui = new ListElementSubGui(element, baseX, baseY + (getHeight() / 4),\n                        maxWidth, maxHeight, gui, container));\n                elementSubGui.init(lastGuiLeft, lastGuiTop);\n            }\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            lastGuiLeft = guiLeft;\n            lastGuiTop = guiTop;\n        }\n\n        @Override\n        public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n            super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n\n            // Output type tooltip\n            this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, element.getValueType());\n        }\n\n        @Override\n        public boolean isRenderTooltip() {\n            return this.renderTooltip;\n        }\n\n        @Override\n        public void setRenderTooltip(boolean renderTooltip) {\n            this.renderTooltip = renderTooltip;\n        }\n    }\n\n    /**\n     * Selection panel for the type.\n     */\n    protected static class SelectionSubGui extends RenderPattern<ValueTypeIngredientsLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> implements IInputListener {\n\n        private WidgetArrowedListField<IngredientComponent<?, ?>> valueTypeSelector = null;\n        private Button arrowAdd;\n\n        public SelectionSubGui(ValueTypeIngredientsLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                               ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        }\n\n        @Override\n        public int getHeight() {\n            return super.getHeight() / 4;\n        }\n\n        protected static List<IngredientComponent<?, ?>> getValueTypes() {\n            // By coincidence, sorting by name (in reverse) is sufficient to achieve the order we want,\n            // for the following known ingredient components:\n            // - minecraft:itemstack\n            // - minecraft:fluidstack\n            // - minecraft:energy\n            // - mekanism:chemicalstack\n            return IngredientComponentHandlers.REGISTRY.getComponents().stream()\n                    .sorted(Comparator.<IngredientComponent<?, ?>, Identifier>comparing(IngredientComponent::getName).reversed())\n                    .toList();\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            valueTypeSelector = new WidgetArrowedListField<>(Minecraft.getInstance().font,\n                    getX() + guiLeft + getWidth() / 2 - 50, getY() + guiTop + 2, 100, 15, true, Component.translatable(\"valuetype.integrateddynamics.value_type\"), true, getValueTypes()) {\n                @Override\n                protected String activeElementToString(IngredientComponent element) {\n                    return IModHelpers.get().getL10NHelpers().localize(element.getTranslationKey());\n                }\n            };\n            valueTypeSelector.setListener(this);\n            //onChanged();\n            int x = guiLeft + getX();\n            int y = guiTop + getY();\n            buttonList.add(arrowAdd = new ButtonText(x + getWidth() - 13, y + getHeight() - 13, 12, 12, Component.translatable(\"gui.integrateddynamics.button.add\"), Component.literal(\"+\"), (b) -> {\n            }, true));\n        }\n\n        @Override\n        public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n            return super.mouseClicked(evt, isDoubleClick) || valueTypeSelector.mouseClicked(evt, isDoubleClick);\n        }\n\n        @Override\n        protected void actionPerformed(Button guibutton) {\n            super.actionPerformed(guibutton);\n            if (guibutton == arrowAdd) {\n                element.setLength(element.getLength() + 1);\n            }\n        }\n\n        @Override\n        public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n            super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n            valueTypeSelector.extractWidgetRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        }\n\n        @Override\n        public void onChanged() {\n            element.setCurrentType(valueTypeSelector.getActiveElement());\n        }\n    }\n\n    /**\n     * Panel for browsing through the list elements and updating them.\n     */\n    protected static class ListElementSubGui extends RenderPattern<ValueTypeIngredientsLPElement, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase> {\n\n        private ButtonArrow arrowLeft;\n        private ButtonArrow arrowRight;\n        private Button arrowRemove;\n\n        private RenderPattern subGui;\n        private IValueTypeLogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, AbstractContainerMenu, ?> subElement;\n\n        public ListElementSubGui(ValueTypeIngredientsLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                 ContainerScreenLogicProgrammerBase<?> gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n            this.subGui = element.getClient().subElementGuis.get(element.getCurrentType()).get(element.getActiveElement());\n            this.subElement = element.getSubElements().get(element.getCurrentType()).get(element.getActiveElement());\n            if(subGui == null) {\n                subGui = (RenderPattern) subElement.getClient().createSubGui(baseX, baseY, maxWidth,\n                        maxHeight / 3 * 2, gui, container);\n                element.getClient().subElementGuis.get(element.getCurrentType()).put(\n                        element.getActiveElement(),\n                        subGui);\n            }\n            int x = getX() + baseX - ValueTypeIngredientsLPElement.OFFSET_X;\n            int y = getY() + baseY - ValueTypeIngredientsLPElement.OFFSET_Y;\n            gui.getMenu().setElementInventory(subElement, x, y);\n            subGuiHolder.addSubGui(subGui);\n\n            // Do the same thing server-side\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerSetElementInventory(\n                            IngredientComponentHandlers.REGISTRY.getComponentHandler(element.getCurrentType()).getValueType(), x, y));\n        }\n\n        @Override\n        public int getHeight() {\n            return (super.getHeight() / 4) * 3;\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            int x = guiLeft + getX();\n            int y = guiTop + getY();\n            buttonList.add(arrowLeft = new ButtonArrow(x, y, Component.translatable(\"gui.cyclopscore.left\"),\n                    b -> element.setActiveElement(element.getActiveElement() - 1), ButtonArrow.Direction.WEST));\n            buttonList.add(arrowRight = new ButtonArrow(x + getWidth() - arrowLeft.getWidth() - 1, y, Component.translatable(\"gui.cyclopscore.right\"),\n                    b -> element.setActiveElement(element.getActiveElement() + 1), ButtonArrow.Direction.EAST));\n            buttonList.add(arrowRemove = new ButtonText(x + (getWidth() / 2) - (arrowLeft.getWidth() / 2), y + getHeight() - 13, 12, 12, Component.translatable(\"gui.integrateddynamics.button.remove\"), Component.literal(\"-\"),\n                    b -> element.removeElement(element.getActiveElement()), true));\n            arrowLeft.active = element.getActiveElement() > 0;\n            arrowRight.active = element.getActiveElement() < element.getLength() - 1;\n            arrowRemove.active = element.getLength() > 0;\n            subElement.getClient().setValueInGui(subGui);\n            subElement.setValueInContainer(subGui.container);\n        }\n\n        @Override\n        public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n            super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n            int x = guiLeft + getX() + (getWidth() / 2);\n            int y = guiTop + getY() + 4;\n            IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, fontRenderer, String.valueOf(element.getActiveElement()), x - 4, y + 2, 10, IModHelpers.get().getBaseHelpers().RGBAToInt(20, 20, 20, 255), false, Font.DisplayMode.NORMAL);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeItemStackLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeItemStackLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeItemStackLPElementClient<V extends IValue> extends ValueTypeLPElementBaseClient<ValueTypeItemStackLPElement<V>> {\n    public ValueTypeItemStackLPElementClient(ValueTypeItemStackLPElement<V> element) {\n        super(element);\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return new ValueTypeItemStackLPElementClient.SubGuiRenderPattern(this.getElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    protected static class SubGuiRenderPattern extends RenderPattern<ValueTypeItemStackLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n            implements IRenderPatternValueTypeTooltip {\n\n        private boolean renderTooltip = true;\n\n        public SubGuiRenderPattern(ValueTypeItemStackLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        }\n\n        @Override\n        public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n            super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n            this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, element.getValueType());\n        }\n\n        @Override\n        public boolean isRenderTooltip() {\n            return this.renderTooltip;\n        }\n\n        @Override\n        public void setRenderTooltip(boolean renderTooltip) {\n            this.renderTooltip = renderTooltip;\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeLPElementBaseClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.IGuiInputElementValueType;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementClient;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic abstract class ValueTypeLPElementBaseClient<T extends ValueTypeLPElementBase>\n        implements ILogicProgrammerElementClient<ISubGuiBox, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> {\n\n    private final T element;\n\n    public ValueTypeLPElementBaseClient(T element) {\n        this.element = element;\n    }\n\n    public T getElement() {\n        return element;\n    }\n\n    @Override\n    public boolean isFocused(ISubGuiBox subGui) {\n        if (subGui instanceof ValueTypeStringLPElementRenderPattern) {\n            return ((ValueTypeStringLPElementRenderPattern) subGui).getTextField().isFocused();\n        }\n        return false;\n    }\n\n    @Override\n    public void setFocused(ISubGuiBox subGui, boolean focused) {\n        if (subGui instanceof ValueTypeStringLPElementRenderPattern) {\n            ((ValueTypeStringLPElementRenderPattern) subGui).getTextField().setFocused(focused);\n        }\n    }\n\n    @Override\n    public abstract ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                            ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container);\n\n    @Override\n    public void setValueInGui(ISubGuiBox subGui) {\n        if (getElement().getInnerGuiElement() != null) {\n            ((IGuiInputElementValueType<ISubGuiBox, ?, ?, ?>) getElement().getInnerGuiElement()).getClient().setValueInGui(subGui, true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeListLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonArrow;\nimport org.cyclops.cyclopscore.client.gui.component.button.ButtonText;\nimport org.cyclops.cyclopscore.client.gui.component.input.IInputListener;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetArrowedListField;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.IValueTypeLogicProgrammerElement;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.RenderPatternCommon;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeListLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerSetElementInventory;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeListLPElementClient extends ValueTypeLPElementBaseClient<ValueTypeListLPElement> {\n\n    private MasterSubGuiRenderPattern masterGui;\n    private Map<Integer, RenderPattern> subElementGuis;\n\n    public ValueTypeListLPElementClient(ValueTypeListLPElement element) {\n        super(element);\n    }\n\n    @Override\n    public void setValueInGui(ISubGuiBox subGui) {\n        if (getElement().getLength() > 0) {\n            masterGui.setSelectedValueType(getElement().getListValueType());\n            setActiveElement(0);\n        }\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return masterGui = new ValueTypeListLPElementClient.MasterSubGuiRenderPattern(this.getElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    public void setActiveElement(int activeElement) {\n        masterGui.setActiveElement(activeElement);\n        masterGui.container.onDirty();\n    }\n\n    public void reset() {\n        this.subElementGuis = Maps.newHashMap();\n    }\n\n    public void removeElement(int index) {\n        Map<Integer, RenderPattern> oldSubElementGuis = subElementGuis;\n        subElementGuis = Maps.newHashMap();\n        for(Map.Entry<Integer, RenderPattern> entry : oldSubElementGuis.entrySet()) {\n            int i = entry.getKey();\n            if(i < index) {\n                subElementGuis.put(i, entry.getValue());\n            } else if(i > index) {\n                subElementGuis.put(i - 1, entry.getValue());\n            }\n        }\n    }\n\n    /**\n     * Sub gui that holds the list element value type panel and the panel for browsing through the elements.\n     */\n    protected static class MasterSubGuiRenderPattern extends RenderPattern<ValueTypeListLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n            implements IRenderPatternValueTypeTooltip {\n\n        private final int baseX;\n        private final int baseY;\n        private final int maxWidth;\n        private final int maxHeight;\n        private final ContainerScreenLogicProgrammerBase gui;\n        private final ContainerLogicProgrammerBase container;\n        private final SelectionSubGui selectionGui;\n\n        protected ListElementSubGui elementSubGui = null;\n        protected int lastGuiLeft;\n        protected int lastGuiTop;\n        private boolean renderTooltip = true;\n\n        public MasterSubGuiRenderPattern(ValueTypeListLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                         ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n            subGuiHolder.addSubGui(this.selectionGui = new SelectionSubGui(element, baseX, baseY - getHeight() / 4, maxWidth, maxHeight, gui, container));\n            this.baseX = baseX;\n            this.baseY = baseY;\n            this.maxWidth = maxWidth;\n            this.maxHeight = maxHeight;\n            this.gui = gui;\n            this.container = container;\n        }\n\n        public void setActiveElement(int index) {\n            if(elementSubGui != null) {\n                subGuiHolder.removeSubGui(elementSubGui);\n            }\n            if(index >= 0) {\n                subGuiHolder.addSubGui(elementSubGui = new ListElementSubGui(element, baseX, baseY,\n                        maxWidth, maxHeight, gui, container));\n                elementSubGui.init(lastGuiLeft, lastGuiTop);\n            }\n        }\n\n        public void setSelectedValueType(IValueType valueType) {\n            this.selectionGui.setActiveElement(valueType);\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            lastGuiLeft = guiLeft;\n            lastGuiTop = guiTop;\n        }\n\n        @Override\n        public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n            super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n\n            // Output type tooltip\n            this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, element.getValueType());\n        }\n\n        @Override\n        public boolean isRenderTooltip() {\n            return this.renderTooltip;\n        }\n\n        @Override\n        public void setRenderTooltip(boolean renderTooltip) {\n            this.renderTooltip = renderTooltip;\n        }\n    }\n\n    /**\n     * Selection panel for the list element value type.\n     */\n    protected static class SelectionSubGui extends RenderPattern<ValueTypeListLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> implements IInputListener {\n\n        private WidgetArrowedListField<IValueType<?>> valueTypeSelector = null;\n        private Button arrowAdd;\n\n        public SelectionSubGui(ValueTypeListLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                               ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        }\n\n        @Override\n        public int getHeight() {\n            return super.getHeight() / 4;\n        }\n\n        protected static List<IValueType<?>> getValueTypes() {\n            List<IValueType<?>> valueTypes = Lists.newArrayList(LogicProgrammerElementTypes.VALUETYPE.getValueTypes());\n            valueTypes.remove(ValueTypes.LIST);\n            valueTypes.add(ValueTypes.CATEGORY_ANY);\n            return valueTypes;\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            valueTypeSelector = new WidgetArrowedListField<IValueType<?>>(Minecraft.getInstance().font,\n                    getX() + guiLeft + getWidth() / 2 - 50, getY() + guiTop + 9, 100, 15, true,\n                    Component.translatable(\"valuetype.integrateddynamics.value_type\"), true, getValueTypes());\n            valueTypeSelector.setListener(this);\n            if (element.getActiveElement() == -1) {\n                onChanged();\n            }\n            int x = guiLeft + getX();\n            int y = guiTop + getY();\n            buttonList.add(arrowAdd = new ButtonText(x + getWidth() - 13, y + 10, 12, 12,\n                    Component.translatable(\"gui.integrateddynamics.button.add\"), Component.literal(\"+\"), b -> {}, true));\n        }\n\n        @Override\n        public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n            return valueTypeSelector.mouseClicked(evt, isDoubleClick) || super.mouseClicked(evt, isDoubleClick);\n        }\n\n        @Override\n        protected void actionPerformed(Button guibutton) {\n            super.actionPerformed(guibutton);\n            if(guibutton == arrowAdd) {\n                element.setLength(element.getLength() + 1);\n            }\n        }\n\n        @Override\n        protected boolean isDrawBackground() {\n            return false;\n        }\n\n        @Override\n        public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n            super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n\n            valueTypeSelector.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n\n            if (element.getActiveElement() >= 0) {\n                int x = guiLeft + getX() + 10;\n                int y = guiTop + getY() + 4;\n                IModHelpers.get().getRenderHelpers().drawScaledCenteredString(guiGraphics, fontRenderer, String.valueOf(element.getActiveElement()), x - 6, y + 12, 10, IModHelpers.get().getBaseHelpers().RGBAToInt(20, 20, 20, 255), false, Font.DisplayMode.NORMAL);\n            }\n        }\n\n        @Override\n        public void onChanged() {\n            IValueType newType = valueTypeSelector.getActiveElement();\n            element.setListValueType(newType);\n            if(arrowAdd != null) {\n                arrowAdd.active = newType != ValueTypes.CATEGORY_ANY;\n            }\n        }\n\n        public void setActiveElement(IValueType valueType) {\n            valueTypeSelector.setListener(null);\n            valueTypeSelector.setActiveElement(valueType);\n            valueTypeSelector.setListener(this);\n        }\n    }\n\n    /**\n     * Panel for browsing through the list elements and updating them.\n     */\n    protected static class ListElementSubGui extends RenderPattern<ValueTypeListLPElement, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase> {\n\n        private ButtonArrow arrowLeft;\n        private ButtonArrow arrowRight;\n        private Button arrowRemove;\n\n        private RenderPattern subGui;\n        private IValueTypeLogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, AbstractContainerMenu, ?> subElement;\n\n        public ListElementSubGui(ValueTypeListLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                 ContainerScreenLogicProgrammerBase<?> gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n            this.subGui = element.getClient().subElementGuis.get(element.getActiveElement());\n            this.subElement = element.getSubElements().get(element.getActiveElement());\n            if(subGui == null) {\n                subGui = (RenderPattern) subElement.getClient().createSubGui(baseX, baseY, maxWidth,\n                        maxHeight, gui, container);\n                element.getClient().subElementGuis.put(\n                        element.getActiveElement(),\n                        subGui);\n            }\n            int x = RenderPatternCommon.calculateX(baseX, maxWidth, subElement.getRenderPattern());\n            int y = RenderPatternCommon.calculateY(baseY, maxHeight, subElement.getRenderPattern());\n            gui.getMenu().setElementInventory(subElement, x, y);\n            subGuiHolder.addSubGui(subGui);\n            if (subGui instanceof IRenderPatternValueTypeTooltip) {\n                ((IRenderPatternValueTypeTooltip) subGui).setRenderTooltip(false);\n            }\n\n            // Do the same thing server-side\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerSetElementInventory(element.getListValueType(), x, y));\n        }\n\n        @Override\n        public void init(int guiLeft, int guiTop) {\n            super.init(guiLeft, guiTop);\n            int x = guiLeft + getX();\n            int y = guiTop + getY();\n            buttonList.add(arrowLeft = new ButtonArrow(x, y, Component.translatable(\"gui.cyclopscore.left\"),\n                    b -> element.setActiveElement(element.getActiveElement() - 1), ButtonArrow.Direction.WEST));\n            buttonList.add(arrowRight = new ButtonArrow(x + getWidth() - arrowLeft.getWidth() - 1, y, Component.translatable(\"gui.cyclopscore.right\"),\n                    b -> element.setActiveElement(element.getActiveElement() + 1), ButtonArrow.Direction.EAST));\n            buttonList.add(arrowRemove = new ButtonText(x + getWidth() - arrowLeft.getWidth() - 1, y + getHeight() - 13, 10, 12, Component.translatable(\"gui.integrateddynamics.button.remove\"), Component.literal(\"-\"),\n                    b -> element.removeElement(element.getActiveElement()), true));\n            arrowLeft.active = element.getActiveElement() > 0;\n            arrowRight.active = element.getActiveElement() < element.getLength() - 1;\n            arrowRemove.active = element.getLength() > 0;\n            container.getTemporaryInputSlots().removeDirtyMarkListener(container);\n            subElement.getClient().setValueInGui(subGui);\n            subElement.setValueInContainer(subGui.container);\n            container.getTemporaryInputSlots().addDirtyMarkListener(container);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeOperatorLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeDropdownListRenderPattern;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeOperatorLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeOperatorLPElementClient extends ValueTypeLPElementBaseClient<ValueTypeOperatorLPElement> {\n    public ValueTypeOperatorLPElementClient(ValueTypeOperatorLPElement element) {\n        super(element);\n    }\n\n    @Override\n    public void setValueInGui(ISubGuiBox subGui) {\n        if (this.getElement().getSelectedOperator() != null) {\n            ((GuiElementValueTypeDropdownListRenderPattern) subGui).getSearchField().setValue(this.getElement().getSelectedOperator().getLocalizedNameFull().getString());\n            ((GuiElementValueTypeDropdownListRenderPattern) subGui).onTyped();\n            ((GuiElementValueTypeDropdownListRenderPattern) subGui).getSearchField().refreshDropdownList();\n        }\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight, ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return new ValueTypeOperatorLPElementClient.RenderPatternOperator(this.getElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    public static class RenderPatternOperator<S extends ISubGuiBox, G extends AbstractContainerScreen, C extends AbstractContainerMenu> extends ValueTypeOperatorLPElementRenderPattern {\n\n        private final ValueTypeOperatorLPElement element;\n\n        public RenderPatternOperator(ValueTypeOperatorLPElement element, int baseX, int baseY, int maxWidth, int maxHeight, ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n            super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n            this.element = element;\n        }\n\n        @Override\n        public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n            super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n            IOperator operator = element.getSelectedOperator();\n            if (operator != null) {\n                int offsetY = 0;\n                for (Component line : ValueTypeOperator.getSignatureLines(operator, true)) {\n                    guiGraphics.text(fontRenderer, line, getX() + guiLeft + 10, getY() + guiTop + 25 + offsetY, IModHelpers.get().getBaseHelpers().RGBAToInt(10, 10, 10, 255), false);\n                    offsetY += fontRenderer.lineHeight;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeOperatorLPElementRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeDropdownListRenderPattern;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeOperatorLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeOperatorLPElementRenderPattern extends GuiElementValueTypeDropdownListRenderPattern<IOperator, RenderPattern, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n        implements IRenderPatternValueTypeTooltip {\n\n    private boolean renderTooltip = true;\n\n    public ValueTypeOperatorLPElementRenderPattern(ValueTypeOperatorLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        super(element.getInnerGuiElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n        this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, element.getValueType());\n    }\n\n    @Override\n    public boolean isRenderTooltip() {\n        return this.renderTooltip;\n    }\n\n    @Override\n    public void setRenderTooltip(boolean renderTooltip) {\n        this.renderTooltip = renderTooltip;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeRecipeLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeRecipeLPElementClient extends ValueTypeLPElementBaseClient<ValueTypeRecipeLPElement> {\n\n    public ValueTypeRecipeLPElementMasterSubGui lastGui;\n\n    public ValueTypeRecipeLPElementClient(ValueTypeRecipeLPElement element) {\n        super(element);\n    }\n\n    public void refreshPropertiesGui(int slot) {\n        if (this.lastGui != null && this.lastGui.isPropertySubGuiActive(slot)) {\n            this.lastGui.propertiesSubGuis.get(slot).loadStateToGui();\n        }\n    }\n\n    public void refreshInputFluidAmountBox() {\n        if (this.lastGui != null && this.lastGui.subGuiRecipe.getInputFluidAmountBox() != null) {\n            this.lastGui.subGuiRecipe.getInputFluidAmountBox().setValue(getElement().getInputFluidAmount());\n        }\n    }\n\n    public void refreshOutputFluidAmountBox() {\n        if (this.lastGui != null && this.lastGui.subGuiRecipe.getOutputFluidAmountBox() != null) {\n            this.lastGui.subGuiRecipe.getOutputFluidAmountBox().setValue(getElement().getOutputFluidAmount());\n        }\n    }\n\n    public void setPropertySubGui(int slotId) {\n        lastGui.setPropertySubGui(slotId);\n    }\n\n    @Override\n    public void setValueInGui(ISubGuiBox subGui) {\n        ValueTypeRecipeLPElement element = getElement();\n        ValueTypeRecipeLPElementRecipeSubGui gui = ((ValueTypeRecipeLPElementMasterSubGui) subGui).getSubGuiRecipe();\n        element.setValueInContainer(gui.container);\n        if (gui.getInputFluidAmountBox() != null) {\n            gui.getInputFluidAmountBox().setValue(element.getInputFluidAmount());\n            gui.getInputEnergyBox().setValue(element.getInputEnergy());\n            gui.getOutputFluidAmountBox().setValue(element.getOutputFluidAmount());\n            gui.getOutputEnergyBox().setValue(element.getOutputEnergy());\n        }\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return lastGui = new ValueTypeRecipeLPElementMasterSubGui(this.getElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeRecipeLPElementMasterSubGui.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.cyclopscore.inventory.container.ContainerExtended;\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGui;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeAdapterLPElementPropertiesSubGui;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\nimport java.util.List;\n\n/**\n * Sub gui that holds the recipe element value type panel and the panel for setting slot properties.\n */\npublic class ValueTypeRecipeLPElementMasterSubGui extends RenderPattern<ValueTypeRecipeLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase> {\n\n    protected final ValueTypeRecipeLPElementRecipeSubGui subGuiRecipe;\n    protected final List<ValueTypeRecipeAdapterLPElementPropertiesSubGui> propertiesSubGuis;\n    protected final int baseX;\n    protected final int baseY;\n\n    protected int lastGuiLeft;\n    protected int lastGuiTop;\n    protected ISubGui subGuiActive;\n\n    public ValueTypeRecipeLPElementMasterSubGui(ValueTypeRecipeLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        this.baseX = baseX;\n        this.baseY = baseY;\n        this.subGuiRecipe = new ValueTypeRecipeLPElementRecipeSubGui(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n        this.propertiesSubGuis = Lists.newArrayList();\n        for (int i = 0; i < 9; i++) {\n            this.propertiesSubGuis.add(new ValueTypeRecipeLPElementPropertiesSubGui(element, baseX, baseY, maxWidth, maxHeight, gui, container, i));\n        }\n        setSubGui(this.subGuiRecipe);\n    }\n\n    public ValueTypeRecipeLPElementRecipeSubGui getSubGuiRecipe() {\n        return subGuiRecipe;\n    }\n\n    public void setSubGui(ISubGui subGui) {\n        if (subGuiActive != null) {\n            subGuiHolder.removeSubGui(subGuiActive);\n        }\n        subGuiHolder.addSubGui(subGui);\n        subGuiActive = subGui;\n        subGuiActive.init(lastGuiLeft, lastGuiTop);\n    }\n\n    public void setRecipeSubGui() {\n        setSubGui(subGuiRecipe);\n\n        // Restore slots\n        for (int i = ValueTypeRecipeLPElement.SLOT_OFFSET; i < element.getRenderPattern().getSlotPositions().length + ValueTypeRecipeLPElement.SLOT_OFFSET; i++) {\n            ContainerExtended.setSlotPosX(container.slots.get(i), 1 + getX() + element.getRenderPattern().getSlotPositions()[i - ValueTypeRecipeLPElement.SLOT_OFFSET].getLeft());\n            ContainerExtended.setSlotPosY(container.slots.get(i), 1 + getY() + element.getRenderPattern().getSlotPositions()[i - ValueTypeRecipeLPElement.SLOT_OFFSET].getRight());\n        }\n    }\n\n    public boolean isPropertySubGuiActive(int index) {\n        return subGuiActive == this.propertiesSubGuis.get(index);\n    }\n\n    public void setPropertySubGui(int index) {\n        setSubGui(this.propertiesSubGuis.get(index));\n\n        // Hide slots (without removing them)\n        for (int i = ValueTypeRecipeLPElement.SLOT_OFFSET; i < element.getRenderPattern().getSlotPositions().length + ValueTypeRecipeLPElement.SLOT_OFFSET; i++) {\n            ContainerExtended.setSlotPosX(container.slots.get(i), -100);\n            ContainerExtended.setSlotPosY(container.slots.get(i), -100);\n        }\n\n        // Place the selected slot in view\n        ContainerExtended.setSlotPosX(container.slots.get(ValueTypeRecipeLPElement.SLOT_OFFSET + index), 1 + getX() + 116);\n        ContainerExtended.setSlotPosY(container.slots.get(ValueTypeRecipeLPElement.SLOT_OFFSET + index), 1 + getY() + 2);\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        super.init(guiLeft, guiTop);\n        lastGuiLeft = guiLeft;\n        lastGuiTop = guiTop;\n    }\n\n    @Override\n    protected boolean drawRenderPattern() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeRecipeLPElementPropertiesSubGui.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeAdapterLPElementPropertiesSubGui;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\nclass ValueTypeRecipeLPElementPropertiesSubGui extends ValueTypeRecipeAdapterLPElementPropertiesSubGui<ValueTypeRecipeLPElement> {\n\n    public ValueTypeRecipeLPElementPropertiesSubGui(ValueTypeRecipeLPElement element, int baseX, int baseY, int maxWidth, int maxHeight, ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container, int slotId) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container, slotId);\n    }\n\n    @Override\n    protected void returnToMainGui() {\n        element.getClient().lastGui.setRecipeSubGui();\n    }\n\n    @Override\n    public ItemStack getSlotContents() {\n        return container.slots.get(slotId + ValueTypeRecipeLPElement.SLOT_OFFSET).getItem();\n    }\n\n    @Override\n    public ItemMatchProperties getSlotProperties() {\n        return getElement().getInputStacks().get(slotId);\n    }\n\n    @Override\n    public void saveGuiToState() {\n        super.saveGuiToState();\n        element.sendSlotPropertiesToServer(slotId, getSlotProperties());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeRecipeLPElementRecipeSubGui.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.input.CharacterEvent;\nimport net.minecraft.client.input.KeyEvent;\nimport net.minecraft.client.input.MouseButtonEvent;\nimport net.minecraft.client.renderer.RenderPipelines;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.TooltipFlag;\nimport org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.client.gui.subgui.SubGuiBox;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeRecipeValueChangedPacket;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeRecipeLPElementRecipeSubGui extends RenderPattern<ValueTypeRecipeLPElement, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n        implements IRenderPatternValueTypeTooltip {\n\n    private boolean renderTooltip = true;\n    private WidgetTextFieldExtended inputFluidAmountBox = null;\n    private WidgetTextFieldExtended inputEnergyBox = null;\n    private WidgetTextFieldExtended outputFluidAmountBox = null;\n    private WidgetTextFieldExtended outputEnergyBox = null;\n\n    public boolean isRenderTooltip() {\n        return renderTooltip;\n    }\n\n    public void setRenderTooltip(boolean renderTooltip) {\n        this.renderTooltip = renderTooltip;\n    }\n\n    public WidgetTextFieldExtended getInputFluidAmountBox() {\n        return inputFluidAmountBox;\n    }\n\n    public WidgetTextFieldExtended getInputEnergyBox() {\n        return inputEnergyBox;\n    }\n\n    public WidgetTextFieldExtended getOutputFluidAmountBox() {\n        return outputFluidAmountBox;\n    }\n\n    public WidgetTextFieldExtended getOutputEnergyBox() {\n        return outputEnergyBox;\n    }\n\n    public ValueTypeRecipeLPElementRecipeSubGui(ValueTypeRecipeLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        super(element, baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    protected static WidgetTextFieldExtended makeTextBox(int componentId, int x, int y, String text) {\n        Font fontRenderer = Minecraft.getInstance().font;\n        int searchWidth = 35;\n\n        WidgetTextFieldExtended box = new WidgetTextFieldExtended(fontRenderer, x, y,\n                searchWidth, fontRenderer.lineHeight + 3, Component.translatable(\"gui.cyclopscore.search\"), true);\n        box.setMaxLength(10);\n        box.setBordered(false);\n        box.setVisible(true);\n        box.setTextColor(ARGB.opaque(16777215));\n        box.setCanLoseFocus(true);\n        box.setValue(text);\n        box.setWidth(searchWidth);\n        return box;\n    }\n\n    @Override\n    public void init(int guiLeft, int guiTop) {\n        super.init(guiLeft, guiTop);\n\n        this.inputFluidAmountBox = makeTextBox(0, guiLeft + getX() + 21, guiTop + getY() + 59, element.getInputFluidAmount());\n        this.inputEnergyBox = makeTextBox(1, guiLeft + getX() + 21, guiTop + getY() + 77, element.getInputEnergy());\n        this.outputFluidAmountBox = makeTextBox(2, guiLeft + getX() + 101, guiTop + getY() + 59, element.getOutputFluidAmount());\n        this.outputEnergyBox = makeTextBox(3, guiLeft + getX() + 101, guiTop + getY() + 77, element.getOutputEnergy());\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n\n        // Output type tooltip\n        this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, element.getValueType());\n\n        // Render the info tooltip when hovering the input item slots\n        for (int slotId = 0; slotId < this.container.slots.size(); ++slotId) {\n            Slot slot = this.container.slots.get(slotId);\n            if (slotId >= ValueTypeRecipeLPElement.SLOT_OFFSET && slotId < 9 + ValueTypeRecipeLPElement.SLOT_OFFSET) {\n                int slotX = slot.x;\n                int slotY = slot.y;\n\n                // Draw tooltips\n                if (gui.isHovering(slotX - 1, slotY - 1, 18, 18, mouseX, mouseY)) {\n                    List<Component> tooltipLines = slot.getItem().isEmpty() ?\n                            Lists.newArrayList() :\n                            slot.getItem().getTooltipLines(\n                                    Item.TooltipContext.of(getContainer().getPlayerIInventory().player.level()),\n                                    getContainer().getPlayerIInventory().player,\n                                    TooltipFlag.NORMAL\n                            );\n                    tooltipLines.add(Component.translatable(\"valuetype.integrateddynamics.ingredients.slot.info\")\n                            .withStyle(ChatFormatting.ITALIC));\n                    gui.drawTooltip(tooltipLines, guiGraphics, mouseX, mouseY);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void renderBg(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, float partialTicks, int mouseX, int mouseY) {\n        super.renderBg(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, partialTicks, mouseX, mouseY);\n\n        // Draw crafting arrow\n        guiGraphics.blit(RenderPipelines.GUI_TEXTURED, SubGuiBox.TEXTURE, guiLeft + getX() + 66, guiTop + getY() + 21, 0, 38, 22, 15, 256, 256);\n\n        inputFluidAmountBox.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        guiGraphics.text(fontRenderer, IModHelpers.get().getL10NHelpers().localize(L10NValues.GENERAL_ENERGY_UNIT) + \":\", guiLeft + getX() + 2, guiTop + getY() + 78, ARGB.opaque(0), false);\n        inputEnergyBox.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        outputFluidAmountBox.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n        guiGraphics.text(fontRenderer, IModHelpers.get().getL10NHelpers().localize(L10NValues.GENERAL_ENERGY_UNIT) + \":\", guiLeft + getX() + 84, guiTop + getY() + 78, ARGB.opaque(0), false);\n        outputEnergyBox.extractRenderState(guiGraphics, mouseX, mouseY, partialTicks);\n    }\n\n    @Override\n    public boolean charTyped(CharacterEvent evt) {\n        if (inputFluidAmountBox.charTyped(evt)) {\n            element.setInputFluidAmount(inputFluidAmountBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getInputFluidAmount(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.INPUT_FLUID));\n            return true;\n        }\n        if (inputEnergyBox.charTyped(evt)) {\n            element.setInputEnergy(inputEnergyBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getInputEnergy(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.INPUT_ENERGY));\n            return true;\n        }\n        if (outputFluidAmountBox.charTyped(evt)) {\n            element.setOutputFluidAmount(outputFluidAmountBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getOutputFluidAmount(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.OUTPUT_FLUID));\n            return true;\n        }\n        if (outputEnergyBox.charTyped(evt)) {\n            element.setOutputEnergy(outputEnergyBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getOutputEnergy(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.OUTPUT_ENERGY));\n            return true;\n        }\n        return super.charTyped(evt);\n    }\n\n    @Override\n    public boolean keyPressed(KeyEvent evt) {\n        if (inputFluidAmountBox.keyPressed(evt)) {\n            element.setInputFluidAmount(inputFluidAmountBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getInputFluidAmount(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.INPUT_FLUID));\n            return true;\n        }\n        if (inputEnergyBox.keyPressed(evt)) {\n            element.setInputEnergy(inputEnergyBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getInputEnergy(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.INPUT_ENERGY));\n            return true;\n        }\n        if (outputFluidAmountBox.keyPressed(evt)) {\n            element.setOutputFluidAmount(outputFluidAmountBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getOutputFluidAmount(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.OUTPUT_FLUID));\n            return true;\n        }\n        if (outputEnergyBox.keyPressed(evt)) {\n            element.setOutputEnergy(outputEnergyBox.getValue());\n            container.onDirty();\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(\n                    new LogicProgrammerValueTypeRecipeValueChangedPacket(element.getOutputEnergy(),\n                            LogicProgrammerValueTypeRecipeValueChangedPacket.Type.OUTPUT_ENERGY));\n            return true;\n        }\n        return super.keyPressed(evt);\n    }\n\n    @Override\n    public boolean mouseClicked(MouseButtonEvent evt, boolean isDoubleClick) {\n        return inputFluidAmountBox.mouseClicked(evt, isDoubleClick)\n                || inputEnergyBox.mouseClicked(evt, isDoubleClick)\n                || outputFluidAmountBox.mouseClicked(evt, isDoubleClick)\n                || outputEnergyBox.mouseClicked(evt, isDoubleClick)\n                || super.mouseClicked(evt, isDoubleClick);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeStringLPElementClient.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeStringLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeStringLPElementClient extends ValueTypeLPElementBaseClient<ValueTypeStringLPElement> {\n    public ValueTypeStringLPElementClient(ValueTypeStringLPElement element) {\n        super(element);\n    }\n\n    @Override\n    public boolean isFocused(ISubGuiBox subGui) {\n        return ((ValueTypeStringLPElementRenderPattern) subGui).getTextField().isFocused();\n    }\n\n    @Override\n    public void setFocused(ISubGuiBox subGui, boolean focused) {\n        ((ValueTypeStringLPElementRenderPattern) subGui).getTextField().setFocused(focused);\n    }\n\n    @Override\n    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,\n                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        return new ValueTypeStringLPElementRenderPattern(this.getElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/client/ValueTypeStringLPElementRenderPattern.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.client;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.client.renderer.texture.TextureManager;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.gui.GuiElementValueTypeStringRenderPattern;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.IRenderPatternValueTypeTooltip;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeStringLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * @author rubensworks\n */\npublic class ValueTypeStringLPElementRenderPattern extends GuiElementValueTypeStringRenderPattern<RenderPattern, ContainerScreenLogicProgrammerBase, ContainerLogicProgrammerBase>\n        implements IRenderPatternValueTypeTooltip {\n\n    private boolean renderTooltip = true;\n\n    public ValueTypeStringLPElementRenderPattern(ValueTypeStringLPElement element, int baseX, int baseY, int maxWidth, int maxHeight,\n                                                 ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {\n        super(element.getInnerGuiElement(), baseX, baseY, maxWidth, maxHeight, gui, container);\n    }\n\n    @Override\n    public void drawGuiContainerForegroundLayer(GuiGraphicsExtractor guiGraphics, int guiLeft, int guiTop, TextureManager textureManager, Font fontRenderer, int mouseX, int mouseY) {\n        super.drawGuiContainerForegroundLayer(guiGraphics, guiLeft, guiTop, textureManager, fontRenderer, mouseX, mouseY);\n        this.drawTooltipForeground(gui, guiGraphics, container, guiLeft, guiTop, mouseX, mouseY, element.getValueType());\n    }\n\n    @Override\n    public boolean isRenderTooltip() {\n        return this.renderTooltip;\n    }\n\n    @Override\n    public void setRenderTooltip(boolean renderTooltip) {\n        this.renderTooltip = renderTooltip;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/event/LogicProgrammerEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.event;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.neoforged.bus.api.Event;\n\n/**\n * A logic programmer event that is posted in the Forge event bus.\n * @author rubensworks\n */\npublic class LogicProgrammerEvent extends Event {\n\n    private final Player player;\n\n    public LogicProgrammerEvent(Player player) {\n        this.player = player;\n    }\n\n    public Player getPlayer() {\n        return player;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/event/LogicProgrammerVariableFacadeCreatedEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.logicprogrammer.event;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\n\nimport javax.annotation.Nullable;\n\n/**\n * A logic programmer event that is posted in the Forge event bus when a new variable is created.\n * @author rubensworks\n */\npublic class LogicProgrammerVariableFacadeCreatedEvent extends LogicProgrammerEvent {\n\n    private final IVariableFacade variableFacade;\n    @Nullable\n    private final BlockState blockState;\n\n    public LogicProgrammerVariableFacadeCreatedEvent(Player player, IVariableFacade variableFacade, BlockState blockState) {\n        super(player);\n        this.variableFacade = variableFacade;\n        this.blockState = blockState;\n    }\n\n    public IVariableFacade getVariableFacade() {\n        return variableFacade;\n    }\n\n    @Nullable\n    public BlockState getBlockState() {\n        return blockState;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/ConcurrentWorldIngredientsProxy.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport org.cyclops.commoncapabilities.api.capability.Capabilities;\nimport org.cyclops.commoncapabilities.api.capability.inventorystate.IInventoryState;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * A thread-safe proxy for reading ingredients within a world.\n * @author rubensworks\n */\npublic class ConcurrentWorldIngredientsProxy<T, M> {\n\n    private final IPositionedAddonsNetworkIngredients<T, M> network;\n    private Set<PartPos> oldPositions;\n    private final Map<PartPos, Integer> states;\n    private final Map<PartPos, Collection<T>> instances;\n    private final Set<PartPos> readStates;\n    private final Set<PartPos> readInstances;\n\n    public ConcurrentWorldIngredientsProxy(IPositionedAddonsNetworkIngredients<T, M> network) {\n        this.network = network;\n        this.oldPositions = Sets.newHashSet();\n        this.states = new ConcurrentHashMap<>();\n        this.instances = new ConcurrentHashMap<>();\n        this.readStates = Collections.newSetFromMap(new ConcurrentHashMap<>());\n        this.readInstances = Collections.newSetFromMap(new ConcurrentHashMap<>());\n    }\n\n    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {\n        return network;\n    }\n\n    public Optional<Integer> getInventoryState(PartPos pos) {\n        Optional<Integer> value = Optional.ofNullable(this.states.get(pos));\n        this.readStates.add(pos);\n        return value;\n    }\n\n    public void setRead(PartPos pos) {\n        this.readInstances.add(pos);\n    }\n\n    public Collection<T> getInstances(PartPos pos) {\n        Collection<T> value = instances.getOrDefault(pos, Collections.emptyList());\n        this.setRead(pos);\n        return value;\n    }\n\n    protected Set<PartPos> getPositions() {\n        return Sets.newHashSet(getNetwork().getPositions());\n    }\n\n    public void onWorldTick() {\n        Set<PartPos> newPositions = getPositions();\n\n        // Do nothing if nothing was read, and no positions were changed\n        boolean positionsChanged = newPositions.size() != this.oldPositions.size() || !newPositions.containsAll(this.oldPositions);\n        if (this.readStates.isEmpty() && this.readInstances.isEmpty() && !positionsChanged) {\n            return;\n        }\n\n        // Update states and ingredients for all positions\n        for (PartPos pos : newPositions) {\n            if (positionsChanged) {\n                this.oldPositions.remove(pos);\n            }\n\n            // Fetch inventory states\n            if (this.readStates.contains(pos) || !this.states.containsKey(pos)) {\n                IInventoryState inventoryState = IModHelpersNeoForge.get().getCapabilityHelpers()\n                        .getCapability(pos.getPos(), pos.getSide(), Capabilities.InventoryState.BLOCK)\n                        .orElse(null);\n                if (inventoryState != null) {\n                    int newState = inventoryState.getState();\n                    Integer previousState = this.states.put(pos, newState);\n\n                    // If we find a state change, make sure that we also reload the instances in this iteration\n                    if (previousState == null || newState != previousState) {\n                        this.readInstances.add(pos);\n                    }\n                } else {\n                    this.states.remove(pos);\n                }\n                this.readStates.remove(pos);\n            }\n\n            // Fetch ingredient instances\n            if (this.readInstances.contains(pos) || !this.instances.containsKey(pos)) {\n                ArrayList<T> instances = Lists.newArrayList(getNetwork().getRawInstances(pos));\n                this.instances.put(pos, instances);\n                this.readInstances.remove(pos);\n            }\n        }\n\n        // Remove positions that are not in the network anymore\n        if (positionsChanged) {\n            for (PartPos oldPosition : this.oldPositions) {\n                this.readStates.remove(oldPosition);\n                this.readInstances.remove(oldPosition);\n                this.states.remove(oldPosition);\n                this.instances.remove(oldPosition);\n            }\n            this.oldPositions = newPositions;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/ConsumingNetworkElementBase.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport org.cyclops.integrateddynamics.api.network.IEnergyConsumingNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\n\n/**\n * Base implementation for an energy consuming network element.\n * @author rubensworks\n */\npublic abstract class ConsumingNetworkElementBase extends NetworkElementBase implements IEnergyConsumingNetworkElement {\n\n    @Override\n    public boolean isUpdate() {\n        return getConsumptionRate() > 0 || super.isUpdate();\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        return 0;\n    }\n\n    @Override\n    public void postUpdate(INetwork network, boolean updated) {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/EnergyNetwork.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.network.IEnergyConsumingNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IEnergyNetwork;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\n\n/**\n * A network that can hold energy.\n * @author rubensworks\n */\npublic class EnergyNetwork extends PositionedAddonsNetworkIngredients<Long, Boolean>\n        implements IEnergyNetwork {\n\n    private INetwork network;\n\n    public INetwork getNetwork() {\n        return network;\n    }\n\n    public void setNetwork(INetwork network) {\n        this.network = network;\n    }\n\n    public EnergyNetwork(IngredientComponent<Long, Boolean> component) {\n        super(component);\n    }\n\n    @Override\n    public boolean canUpdate(INetworkElement element) {\n        if(!(element instanceof IEnergyConsumingNetworkElement)) return true;\n        int multiplier = GeneralConfig.energyConsumptionMultiplier;\n        if(multiplier == 0) return true;\n        int consumptionRate = ((IEnergyConsumingNetworkElement) element).getConsumptionRate() * multiplier;\n        return getChannel(element.getChannel()).extract(consumptionRate, true) == consumptionRate;\n    }\n\n    @Override\n    public void onSkipUpdate(INetworkElement element) {\n        if(element instanceof IEnergyConsumingNetworkElement consumingNetworkElement) {\n            consumingNetworkElement.postUpdate(getNetwork(), false);\n        }\n    }\n\n    @Override\n    public void postUpdate(INetworkElement element) {\n        if(element instanceof IEnergyConsumingNetworkElement consumingNetworkElement) {\n            int multiplier = GeneralConfig.energyConsumptionMultiplier;\n            if (multiplier > 0) {\n                int consumptionRate = consumingNetworkElement.getConsumptionRate() * multiplier;\n                getChannel(element.getChannel()).extract(consumptionRate, false);\n            }\n            consumingNetworkElement.postUpdate(getNetwork(), true);\n        }\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        int multiplier = GeneralConfig.energyConsumptionMultiplier;\n        if(multiplier == 0) return 0;\n        int consumption = 0;\n        for(INetworkElement element : getNetwork().getElements()) {\n            if (element instanceof IEnergyConsumingNetworkElement consuming) {\n                consumption += consuming.getConsumptionRate() * multiplier;\n            }\n        }\n        return consumption;\n    }\n\n    @Override\n    public long getRateLimit() {\n        return GeneralConfig.energyRateLimit;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IIngredientChannelInsertPreConsumer.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport net.neoforged.neoforge.transfer.transaction.TransactionContext;\n\nimport javax.annotation.Nonnull;\n\n/**\n * Allows (partially) consuming an ingredient before it is inserted into the network.\n * @author rubensworks\n */\npublic interface IIngredientChannelInsertPreConsumer<T> {\n\n    /**\n     * Called before an ingredient is inserted into the network.\n     * If nothing needs to be consumed, the same instance can be returned.\n     * @param channel The network channel.\n     * @param ingredient The ingredient instance.\n     * @param transaction The current transaction.\n     * @return The remaining ingredient instance.\n     */\n    public T insert(int channel, @Nonnull T ingredient, TransactionContext transaction);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientChannelAdapter.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Lists;\nimport net.neoforged.neoforge.transfer.transaction.SnapshotJournal;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport net.neoforged.neoforge.transfer.transaction.TransactionContext;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;\nimport org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;\nimport org.cyclops.integrateddynamics.api.network.INetworkIngredientsChannel;\nimport org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\nimport org.cyclops.integrateddynamics.api.network.PositionedAddonsNetworkIngredientsFilter;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport javax.annotation.Nonnull;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\n/**\n * An abstract {@link IIngredientComponentStorage} that wraps over a {@link IPositionedAddonsNetworkIngredients}.\n *\n * @param <T> The instance type.\n * @param <M> The matching condition parameter.\n */\npublic abstract class IngredientChannelAdapter<T, M> implements INetworkIngredientsChannel<T, M> {\n\n    private final IPositionedAddonsNetworkIngredients<T, M> network;\n    private final int channel;\n\n    private boolean limitsEnabled;\n\n    public IngredientChannelAdapter(PositionedAddonsNetworkIngredients<T, M> network, int channel) {\n        this.network = network;\n        this.channel = channel;\n\n        this.limitsEnabled = true;\n    }\n\n    public void enableLimits() {\n        this.limitsEnabled = true;\n    }\n\n    public void disableLimits() {\n        this.limitsEnabled = false;\n    }\n\n    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {\n        return network;\n    }\n\n    public int getChannel() {\n        return channel;\n    }\n\n    @Override\n    public IngredientComponent<T, M> getComponent() {\n        return network.getComponent();\n    }\n\n    protected abstract Iterator<PartPos> getNonFullPositions();\n    protected abstract Iterator<PartPos> getAllPositions();\n    protected abstract Iterator<PartPos> getNonEmptyPositions();\n    protected abstract Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags);\n\n    @Override\n    public Iterable<PartPos> findNonFullPositions() {\n        return () -> getPartPosIteratorData(this::getNonFullPositions, channel).getRight();\n    }\n\n    @Override\n    public Iterable<PartPos> findAllPositions() {\n        return () -> getPartPosIteratorData(this::getAllPositions, channel).getRight();\n    }\n\n    @Override\n    public Iterable<PartPos> findNonEmptyPositions() {\n        return () -> getPartPosIteratorData(this::getNonEmptyPositions, channel).getRight();\n    }\n\n    @Override\n    public Iterable<PartPos> findMatchingPositions(@Nonnull T prototype, M matchFlags) {\n        return () -> getPartPosIteratorData(() -> this.getMatchingPositions(prototype, matchFlags), channel).getRight();\n    }\n\n    @Override\n    public long getMaxQuantity() {\n        long sum = 0;\n        Iterator<PartPos> it = getAllPositions();\n        while (it.hasNext() && sum < Long.MAX_VALUE) {\n            PartPos pos = it.next();\n            // Skip if the position is not loaded\n            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {\n                continue;\n            }\n            this.network.disablePosition(pos);\n            try {\n                sum = Math.addExact(sum, this.network.getPositionedStorage(pos).getMaxQuantity());\n            } catch (ArithmeticException e) {\n                sum = Long.MAX_VALUE; // If we had an overflow, we're already at max quantity.\n            }\n            this.network.enablePosition(pos);\n        }\n\n        // Schedule an observation, as since this method is called, there may be a need for changes later on.\n        scheduleObservation();\n\n        return sum;\n    }\n\n    protected Pair<IPartPosIteratorHandler, Iterator<PartPos>> getPartPosIteratorData(Supplier<Iterator<PartPos>> iteratorSupplier, int channel) {\n        IPartPosIteratorHandler handler = network.getPartPosIteratorHandler();\n        if (handler == null) {\n            handler = PartPosIteratorHandlerDummy.INSTANCE;\n        } else {\n            handler = handler.clone();\n        }\n        return Pair.of(handler, handler.handleIterator(iteratorSupplier, channel));\n    }\n\n    protected void savePartPosIteratorHandler(IPartPosIteratorHandler partPosIteratorHandler, TransactionContext transaction) {\n        new PartPosIteratorHandledJournal(partPosIteratorHandler.clone()).updateSnapshots(transaction);\n        network.setPartPosIteratorHandler(partPosIteratorHandler);\n    }\n\n    protected void markStoragePositionChanged(int channel, PartPos targetPos) {\n        this.network.scheduleObservationForced(channel, targetPos);\n    }\n\n    @Override\n    public T insert(@Nonnull T ingredient, TransactionContext transaction) {\n        // First run the ingredient instance through the pre-consumers.\n        for (IIngredientChannelInsertPreConsumer<T> insertPreConsumer : network.getInsertPreConsumers()) {\n            ingredient = insertPreConsumer.insert(this.channel, ingredient, transaction);\n        }\n\n        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();\n\n        // Quickly return if the to-be-inserted ingredient was already empty\n        if (matcher.isEmpty(ingredient)) {\n            return ingredient;\n        }\n\n        // Limit rate\n        long skippedQuantity = 0;\n        T ingredientOriginal = ingredient;\n        if (this.limitsEnabled) {\n            long limit = network.getRateLimit();\n            long currentQuantity = matcher.getQuantity(ingredient);\n            if (currentQuantity > limit) {\n                ingredient = matcher.withQuantity(ingredient, limit);\n                skippedQuantity = currentQuantity - limit;\n            }\n        }\n\n        // Try inserting the ingredient at all positions that are not full,\n        // until the ingredient becomes completely empty.\n        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonFullPositions, channel);\n        Iterator<PartPos> it = partPosIteratorData.getRight();\n        while (it.hasNext()) {\n            PartPos pos = it.next();\n\n            // Skip if the position is not loaded or disabled\n            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {\n                continue;\n            }\n\n            // Skip if a filter was set that doesn't match the ingredient\n            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);\n            if (filter != null && !filter.testInsertion(ingredient)) {\n                continue;\n            }\n\n            this.network.disablePosition(pos);\n            long quantityBefore = matcher.getQuantity(ingredient);\n            ingredient = this.network.getPositionedStorage(pos).insert(ingredient, transaction);\n            long quantityAfter = matcher.getQuantity(ingredient);\n            this.network.enablePosition(pos);\n            if (quantityBefore != quantityAfter) {\n                new MarkStoragePositionChangedOnRootCommitJournal(pos).updateSnapshots(transaction);\n            }\n            if (matcher.isEmpty(ingredient)) {\n                break;\n            }\n        }\n\n        // Re-add skipped quantity to response if applicable\n        if (skippedQuantity > 0) {\n            // Modify ingredientOriginal instead of ingredient, because ingredient may be EMPTY.\n            ingredient = matcher.withQuantity(ingredientOriginal, skippedQuantity + matcher.getQuantity(ingredient));\n        }\n\n        savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);\n\n        return ingredient;\n    }\n\n    @Override\n    public T extract(long maxQuantity, TransactionContext transaction) {\n        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();\n\n        // Limit rate\n        if (this.limitsEnabled) {\n            maxQuantity = (int) Math.min(maxQuantity, network.getRateLimit());\n        }\n\n        // Try extracting from all non-empty positions\n        // until one succeeds.\n        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonEmptyPositions, channel);\n        Iterator<PartPos> it = partPosIteratorData.getRight();\n        while (it.hasNext()) {\n            PartPos pos = it.next();\n\n            // Skip if the position is not loaded or disabled\n            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {\n                continue;\n            }\n\n            // Obtain storage\n            this.network.disablePosition(pos);\n            IIngredientComponentStorage<T, M> positionedStorage = this.network.getPositionedStorage(pos);\n\n            // If we do an effective extraction, first simulate to check if it matches the filter\n            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);\n            if (filter != null) {\n                try (var tx = Transaction.open(transaction)) {\n                    T extractedSimulated = positionedStorage.extract(maxQuantity, tx);\n                    if (!filter.testExtraction(extractedSimulated)) {\n                        continue;\n                    }\n                }\n            }\n\n            T extracted = positionedStorage.extract(maxQuantity, transaction);\n\n            this.network.enablePosition(pos);\n            if (!matcher.isEmpty(extracted)) {\n                new MarkStoragePositionChangedOnRootCommitJournal(pos).updateSnapshots(transaction);\n                savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);\n                return extracted;\n            }\n        }\n\n        savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);\n\n        // Schedule an observation if nothing was extracted, because the index may not be initialized yet.\n        scheduleObservation();\n\n        return matcher.getEmptyInstance();\n    }\n\n    @Override\n    public T extract(@Nonnull T prototype, M matchFlags, TransactionContext transaction) {\n        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();\n        boolean checkQuantity = matcher.hasCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());\n\n        // Limit rate\n        if (this.limitsEnabled) {\n            long limit = network.getRateLimit();\n            if (matcher.getQuantity(prototype) > limit) {\n                // Fail immediately if we require more than the limit\n                if (checkQuantity) {\n                    return matcher.getEmptyInstance();\n                }\n\n                // Otherwise, we reduce our requested quantity\n                prototype = matcher.withQuantity(prototype, limit);\n            }\n        }\n\n        final T prototypeFinal = prototype;\n        long requiredQuantity = matcher.getQuantity(prototypeFinal);\n\n        // Modify our match condition that will be used to test each separate interface\n        if (checkQuantity) {\n            matchFlags = matcher.withoutCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());\n        }\n        M finalMatchFlags = matchFlags;\n\n        // Maintain a temporary mapping of prototype items to their total count over all positions,\n        // plus the list of positions in which they are present.\n        IIngredientMapMutable<T, M, Pair<Wrapper<Long>, List<PartPos>>> validInstancesCollapsed = new IngredientHashMap<>(getComponent());\n\n        // Try extracting from all positions that match with the given conditions\n        // until one succeeds.\n        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(() -> this.getMatchingPositions(prototypeFinal, finalMatchFlags), channel);\n        Iterator<PartPos> it = partPosIteratorData.getRight();\n        while (it.hasNext()) {\n            PartPos pos = it.next();\n\n            // Skip if the position is not loaded or disabled\n            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {\n                continue;\n            }\n\n            // Do a simulated extraction\n            this.network.disablePosition(pos);\n            T extractedSimulated;\n            try (var tx = Transaction.open(transaction)) {\n                extractedSimulated = this.network.getPositionedStorage(pos).extract(prototypeFinal, finalMatchFlags, tx);\n            }\n            this.network.enablePosition(pos);\n            T storagePrototype = getComponent().getMatcher().withQuantity(extractedSimulated, 1);\n\n            // Skip if a filter was set that doesn't match the simulated extraction\n            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);\n            if (filter != null && !filter.testExtraction(extractedSimulated)) {\n                continue;\n            }\n\n            // Get existing value from temporary mapping\n            Pair<Wrapper<Long>, List<PartPos>> existingValue = validInstancesCollapsed.get(storagePrototype);\n            if (existingValue == null) {\n                existingValue = Pair.of(new Wrapper<>(0L), Lists.newLinkedList());\n                validInstancesCollapsed.put(storagePrototype, existingValue);\n            }\n\n            // Update the counter and pos-list for our prototype\n            long newCount = existingValue.getLeft().get() + matcher.getQuantity(extractedSimulated);\n            existingValue.getLeft().set(newCount);\n            existingValue.getRight().add(pos);\n\n            // If the count is sufficient for our query, return\n            if (newCount >= requiredQuantity) {\n                // Save the iterator state before returning\n                savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);\n                existingValue.getLeft().set(requiredQuantity);\n                return finalizeExtraction(storagePrototype, matchFlags, existingValue, transaction);\n            }\n        }\n\n        // If we reach this point, then our effective count is below requiredQuantity\n\n        // Save the iterator state before returning\n        savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);\n\n        // Fail if we required an exact quantity\n        if (checkQuantity) {\n            return matcher.getEmptyInstance();\n        }\n\n        // Extract for the instance that had the most matches if we didn't require an exact quantity\n        Pair<Wrapper<Long>, List<PartPos>> maxValue = Pair.of(new Wrapper<>(0L), Lists.newArrayList());\n        T maxInstance = matcher.getEmptyInstance();\n        for (Map.Entry<T, Pair<Wrapper<Long>, List<PartPos>>> entry : validInstancesCollapsed) {\n            if (entry.getValue().getLeft().get() > maxValue.getLeft().get()) {\n                maxInstance = entry.getKey();\n                maxValue = entry.getValue();\n            }\n        }\n\n        // Schedule an observation, as since this method is called, there may be a need for changes later on.\n        scheduleObservation();\n\n        return finalizeExtraction(maxInstance, matchFlags, maxValue, transaction);\n    }\n\n    protected T finalizeExtraction(T instancePrototype, M matchFlags, Pair<Wrapper<Long>, List<PartPos>> value,\n                                   TransactionContext transaction) {\n        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();\n        long extractedCount = value.getLeft().get();\n        if (extractedCount > 0) {\n            long toExtract = extractedCount;\n            for (PartPos pos : value.getRight()) {\n                // Update the remaining prototype quantity for this iteration\n                instancePrototype = matcher.withQuantity(instancePrototype, toExtract);\n\n                this.network.disablePosition(pos);\n                T extracted = this.network.getPositionedStorage(pos).extract(instancePrototype, matchFlags, transaction);\n                this.network.enablePosition(pos);\n                markStoragePositionChanged(channel, pos);\n                long thisExtractedAmount = matcher.getQuantity(extracted);\n                toExtract -= thisExtractedAmount;\n            }\n            // Quick heuristic check to see if 'storage' did not lie during its simulation\n            if (toExtract != 0) {\n                /*IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\n                        \"A storage resulted in inconsistent simulated and non-simulated output. Storages: %s\", value.getRight()));*/\n                // This is not such a huge problem actually, so just make sure our output is correct.\n                extractedCount -= toExtract;\n            }\n        }\n        return getComponent().getMatcher().withQuantity(instancePrototype, extractedCount);\n    }\n\n    protected void scheduleObservation() {\n        this.network.scheduleObservation();\n    }\n\n    class MarkStoragePositionChangedOnRootCommitJournal extends SnapshotJournal<Void> {\n        private final PartPos pos;\n\n        MarkStoragePositionChangedOnRootCommitJournal(PartPos pos) {\n            this.pos = pos;\n        }\n\n        @Override\n        protected Void createSnapshot() {\n            return null;\n        }\n\n        @Override\n        protected void revertToSnapshot(Void unused) {\n\n        }\n\n        @Override\n        protected void onRootCommit(Void originalState) {\n            super.onRootCommit(originalState);\n            markStoragePositionChanged(channel, pos);\n        }\n    }\n\n    class PartPosIteratorHandledJournal extends SnapshotJournal<Void> {\n        private final IPartPosIteratorHandler iteratorHandler;\n\n        PartPosIteratorHandledJournal(IPartPosIteratorHandler iteratorHandler) {\n            this.iteratorHandler = iteratorHandler;\n        }\n\n        @Override\n        protected Void createSnapshot() {\n            return null;\n        }\n\n        @Override\n        protected void revertToSnapshot(Void unused) {\n            network.setPartPosIteratorHandler(iteratorHandler);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientChannelAdapterWrapperSlotted.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Iterators;\nimport it.unimi.dsi.fastutil.ints.Int2IntMap;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport net.neoforged.neoforge.transfer.transaction.TransactionContext;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageSlotted;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\nimport org.cyclops.integrateddynamics.api.network.PositionedAddonsNetworkIngredientsFilter;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport javax.annotation.Nonnull;\nimport java.util.Iterator;\n\n/**\n * A slotted wrapper over {@link IngredientChannelAdapter}.\n *\n * It exposes slots by chaining storages sequentially, not using the index.\n *\n * @param <T> The instance type.\n * @param <M> The matching condition parameter.\n *\n * @author rubensworks\n */\npublic class IngredientChannelAdapterWrapperSlotted<T, M> implements IIngredientComponentStorageSlotted<T, M> {\n\n    private final IngredientChannelAdapter<T, M> channel;\n    private final Int2IntMap cacheChannelSlots;\n\n    public IngredientChannelAdapterWrapperSlotted(IngredientChannelAdapter<T, M> channel, Int2IntMap cacheChannelSlots) {\n        this.channel = channel;\n        this.cacheChannelSlots = cacheChannelSlots;\n    }\n\n    protected static int getIngredientComponentStorageSize(IIngredientComponentStorage<?, ?> storage) {\n        if (storage instanceof IIngredientComponentStorageSlotted) {\n            return ((IIngredientComponentStorageSlotted<?, ?>) storage).getSlots();\n        } else {\n            return Iterators.size(storage.iterator()) + 1;\n        }\n    }\n\n    @Override\n    public int getSlots() {\n        int slots = this.cacheChannelSlots.getOrDefault(this.channel.getChannel(), -1);\n        if (slots != -1) {\n            return slots;\n        }\n\n        slots = 0;\n        IPositionedAddonsNetworkIngredients<T, M> network = this.channel.getNetwork();\n\n        boolean hasDisabledPosition = false;\n        for (PartPos pos : network.getPositions(this.channel.getChannel())) {\n            // Skip if the position is not loaded or disabled\n            if (!pos.getPos().isLoaded()) {\n                continue;\n            }\n            if (network.isPositionDisabled(pos)) {\n                hasDisabledPosition = true;\n                continue;\n            }\n            network.disablePosition(pos);\n            IIngredientComponentStorage<T, M> storage = network.getPositionedStorage(pos);\n            slots = IModHelpers.get().getBaseHelpers().addSafe(slots, getIngredientComponentStorageSize(storage));\n            network.enablePosition(pos);\n        }\n\n        if (!hasDisabledPosition) {\n            this.cacheChannelSlots.put(this.channel.getChannel(), slots);\n        }\n        return slots;\n    }\n\n    protected Triple<IIngredientComponentStorage<T, M>, Integer, PartPos> getStorageAndRelativeSlot(int slot) {\n        IPositionedAddonsNetworkIngredients<T, M> network = this.channel.getNetwork();\n\n        for (PartPos pos : network.getPositions(this.channel.getChannel())) {\n            // Skip if the position is not loaded or disabled\n            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {\n                continue;\n            }\n            network.disablePosition(pos);\n            IIngredientComponentStorage<T, M> storage = network.getPositionedStorage(pos);\n            int storageSize = getIngredientComponentStorageSize(storage);\n            network.enablePosition(pos);\n            if (slot < storageSize) {\n                return Triple.of(storage, slot, pos);\n            } else {\n                slot -= storageSize;\n            }\n        }\n\n        return Triple.of(null, -1, null);\n    }\n\n    @Override\n    public T getSlotContents(int slotAbsolute) {\n        Triple<IIngredientComponentStorage<T, M>, Integer, PartPos> storageAndSlot = getStorageAndRelativeSlot(slotAbsolute);\n        IIngredientComponentStorage<T, M> storage = storageAndSlot.getLeft();\n        int slotRelative = storageAndSlot.getMiddle();\n        PartPos pos = storageAndSlot.getRight();\n        if (storage == null) {\n            return getComponent().getMatcher().getEmptyInstance();\n        }\n\n        if (storage instanceof IIngredientComponentStorageSlotted) {\n            return ((IIngredientComponentStorageSlotted<T, M>) storage).getSlotContents(slotRelative);\n        } else {\n            try {\n                T ingredient = Iterators.get(storage.iterator(), slotRelative);\n                PositionedAddonsNetworkIngredientsFilter<T> filter = this.channel.getNetwork().getPositionedStorageFilter(pos);\n                if (filter != null && !filter.testView(ingredient)) {\n                    return getComponent().getMatcher().getEmptyInstance();\n                }\n                return ingredient;\n            } catch (IndexOutOfBoundsException e) {\n                return getComponent().getMatcher().getEmptyInstance();\n            }\n        }\n    }\n\n    @Override\n    public long getMaxQuantity(int slotAbsolute) {\n        Triple<IIngredientComponentStorage<T, M>, Integer, PartPos> storageAndSlot = getStorageAndRelativeSlot(slotAbsolute);\n        IIngredientComponentStorage<T, M> storage = storageAndSlot.getLeft();\n        int slotRelative = storageAndSlot.getMiddle();\n        if (storage == null) {\n            return 0;\n        }\n\n        if (storage instanceof IIngredientComponentStorageSlotted) {\n            return ((IIngredientComponentStorageSlotted<T, M>) storage).getMaxQuantity(slotRelative);\n        } else {\n            return IModHelpers.get().getBaseHelpers().castSafe(getComponent().getMatcher().getMaximumQuantity());\n        }\n    }\n\n    @Override\n    public T insert(int slotAbsolute, @Nonnull T ingredient, TransactionContext transaction) {\n        Triple<IIngredientComponentStorage<T, M>, Integer, PartPos> storageAndSlot = getStorageAndRelativeSlot(slotAbsolute);\n        IIngredientComponentStorage<T, M> storage = storageAndSlot.getLeft();\n        int slotRelative = storageAndSlot.getMiddle();\n        PartPos pos = storageAndSlot.getRight();\n        if (storage == null) {\n            return ingredient;\n        }\n\n        PositionedAddonsNetworkIngredientsFilter<T> filter = this.channel.getNetwork().getPositionedStorageFilter(pos);\n        if (filter != null && !filter.testInsertion(ingredient)) {\n            return ingredient;\n        }\n\n        if (storage instanceof IIngredientComponentStorageSlotted) {\n            return ((IIngredientComponentStorageSlotted<T, M>) storage).insert(slotRelative, ingredient, transaction);\n        } else {\n            return storage.insert(ingredient, transaction);\n        }\n    }\n\n    @Override\n    public T extract(int slotAbsolute, long maxQuantity, TransactionContext transaction) {\n        Triple<IIngredientComponentStorage<T, M>, Integer, PartPos> storageAndSlot = getStorageAndRelativeSlot(slotAbsolute);\n        IIngredientComponentStorage<T, M> storage = storageAndSlot.getLeft();\n        int slotRelative = storageAndSlot.getMiddle();\n        PartPos pos = storageAndSlot.getRight();\n        if (storage == null) {\n            return getComponent().getMatcher().getEmptyInstance();\n        }\n\n        // If we do an effective extraction, first simulate to check if it matches the filter\n        PositionedAddonsNetworkIngredientsFilter<T> filter = this.channel.getNetwork().getPositionedStorageFilter(pos);\n        if (filter != null) {\n            try (var tx = Transaction.open(transaction)) {\n                T extractedSimulated;\n                if (storage instanceof IIngredientComponentStorageSlotted) {\n                    extractedSimulated = ((IIngredientComponentStorageSlotted<T, M>) storage).extract(slotRelative, maxQuantity, tx);\n                } else {\n                    extractedSimulated = storage.extract(maxQuantity, tx);\n                }\n                if (!filter.testExtraction(extractedSimulated)) {\n                    return getComponent().getMatcher().getEmptyInstance();\n                }\n            }\n        }\n\n        T extracted;\n        if (storage instanceof IIngredientComponentStorageSlotted) {\n            extracted = ((IIngredientComponentStorageSlotted<T, M>) storage).extract(slotRelative, maxQuantity, transaction);\n        } else {\n            extracted = storage.extract(maxQuantity, transaction);\n        }\n\n        return extracted;\n    }\n\n    @Override\n    public IngredientComponent<T, M> getComponent() {\n        return channel.getComponent();\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        return channel.iterator();\n    }\n\n    @Override\n    public Iterator<T> iterator(@Nonnull T prototype, M matchCondition) {\n        return channel.iterator(prototype, matchCondition);\n    }\n\n    @Override\n    public long getMaxQuantity() {\n        return channel.getMaxQuantity();\n    }\n\n    @Override\n    public T insert(@Nonnull T ingredient, TransactionContext transaction) {\n        return channel.insert(ingredient, transaction);\n    }\n\n    @Override\n    public T extract(@Nonnull T prototype, M matchCondition, TransactionContext transaction) {\n        return channel.extract(prototype, matchCondition, transaction);\n    }\n\n    @Override\n    public T extract(long maxQuantity, TransactionContext transaction) {\n        return channel.extract(maxQuantity, transaction);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientChannelIndexed.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientPositionsIndex;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport javax.annotation.Nonnull;\nimport java.util.Iterator;\n\n/**\n * An ingredient channel that exploits the network's index.\n *\n * @param <T> The instance type.\n * @param <M> The matching condition parameter.\n */\npublic class IngredientChannelIndexed<T, M> extends IngredientChannelAdapter<T, M> {\n\n    private final IIngredientPositionsIndex<T, M> index;\n\n    public IngredientChannelIndexed(PositionedAddonsNetworkIngredients<T, M> network, int channel,\n                                    IIngredientPositionsIndex<T, M> index) {\n        super(network, channel);\n        this.index = index;\n    }\n\n    public IIngredientPositionsIndex<T, M> getIndex() {\n        return index;\n    }\n\n    @Override\n    protected Iterator<PartPos> getNonFullPositions() {\n        this.scheduleObservation();\n        return this.getNetwork().getPositions(getChannel()).iterator();\n    }\n\n    @Override\n    protected Iterator<PartPos> getAllPositions() {\n        this.scheduleObservation();\n        return this.getNetwork().getPositions(getChannel()).iterator();\n    }\n\n    @Override\n    protected Iterator<PartPos> getNonEmptyPositions() {\n        this.scheduleObservation();\n        return this.index.getNonEmptyPositions();\n    }\n\n    @Override\n    protected Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags) {\n        this.scheduleObservation();\n        return this.index.getPositions(prototype, matchFlags);\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        this.scheduleObservation();\n        return this.index.iterator();\n    }\n\n    @Override\n    public Iterator<T> iterator(@Nonnull T prototype, M matchFlags) {\n        this.scheduleObservation();\n        return this.index.iterator(prototype, matchFlags);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientChannelPositioned.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Iterators;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\nimport org.cyclops.integrateddynamics.api.network.PositionedAddonsNetworkIngredientsFilter;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport javax.annotation.Nonnull;\nimport java.util.Iterator;\n\n/**\n * An ingredient channel that naively iterates over all positions in the network.\n *\n * @param <T> The instance type.\n * @param <M> The matching condition parameter.\n */\npublic class IngredientChannelPositioned<T, M> extends IngredientChannelAdapter<T, M> {\n\n    public IngredientChannelPositioned(PositionedAddonsNetworkIngredients<T, M> network, int channel) {\n        super(network, channel);\n    }\n\n    @Override\n    protected Iterator<PartPos> getNonFullPositions() {\n        return getAllPositions();\n    }\n\n    @Override\n    protected Iterator<PartPos> getAllPositions() {\n        this.scheduleObservation();\n        return getNetwork().getPositions(getChannel()).iterator();\n    }\n\n    @Override\n    protected Iterator<PartPos> getNonEmptyPositions() {\n        return getAllPositions();\n    }\n\n    @Override\n    protected Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags) {\n        return getAllPositions();\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        this.scheduleObservation();\n        return new PositionedIngredientIterator<>(getNetwork(), getNetwork().getPositions(getChannel()).iterator(),\n                getComponent().getMatcher().getEmptyInstance(), getComponent().getMatcher().getAnyMatchCondition());\n    }\n\n    @Override\n    public Iterator<T> iterator(@Nonnull T prototype, M matchFlags) {\n        this.scheduleObservation();\n        return new PositionedIngredientIterator<>(getNetwork(), getNetwork().getPositions(getChannel()).iterator(),\n                prototype, matchFlags);\n    }\n\n    public static class PositionedIngredientIterator<T, M> implements Iterator<T> {\n\n        private final IPositionedAddonsNetworkIngredients<T, M> network;\n        private final Iterator<PartPos> it;\n        private final T prototype;\n        private final M matchFlags;\n        private Iterator<T> lastPos;\n\n        public PositionedIngredientIterator(IPositionedAddonsNetworkIngredients<T, M> network,\n                                            Iterator<PartPos> it, T prototype, M matchFlags) {\n            this.network = network;\n            this.it = it;\n            this.prototype = prototype;\n            this.matchFlags = matchFlags;\n            prepareLastPos();\n        }\n\n        protected void prepareLastPos() {\n            do {\n                if (it.hasNext()) {\n                    PartPos pos = it.next();\n                    // Skip if the position is not loaded\n                    if (!pos.getPos().isLoaded()) {\n                        continue;\n                    }\n                    this.lastPos = this.network.getPositionedStorage(pos).iterator(prototype, matchFlags);\n                    PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);\n                    if (filter != null) {\n                        this.lastPos = Iterators.filter(this.lastPos, filter::testView);\n                    }\n                } else {\n                    this.lastPos = null;\n                }\n            } while (this.lastPos != null && !this.lastPos.hasNext());\n        }\n\n        @Override\n        public boolean hasNext() {\n            return lastPos != null;\n        }\n\n        @Override\n        public T next() {\n            T next = lastPos.next();\n            if (!lastPos.hasNext()) {\n                prepareLastPos();\n            }\n            return next;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientObserver.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Iterators;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;\nimport it.unimi.dsi.fastutil.ints.IntSet;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.event.server.ServerStoppingEvent;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.ingredient.collection.diff.IngredientCollectionDiff;\nimport org.cyclops.cyclopscore.ingredient.collection.diff.IngredientCollectionDiffManager;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentStorageObservable;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;\n\nimport javax.annotation.Nullable;\nimport java.util.*;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.function.Consumer;\n\n/**\n * Instances of this class are able to watch ingredient positions and emit diffs.\n *\n * @author rubensworks\n */\npublic class IngredientObserver<T, M> {\n\n    private static final ExecutorService WORKER_POOL = Executors.newFixedThreadPool(GeneralConfig.ingredientNetworkObserverThreads);\n    static {\n        NeoForge.EVENT_BUS.addListener((Consumer<ServerStoppingEvent>) event -> {\n            if (event.getServer().isDedicatedServer()) {\n                WORKER_POOL.shutdown();\n            }\n        });\n    }\n\n    private final IPositionedAddonsNetworkIngredients<T, M> network;\n    private final ConcurrentWorldIngredientsProxy<T, M> worldProxy;\n\n    private final List<IIngredientComponentStorageObservable.IIndexChangeObserver<T, M>> changeObservers;\n    private final Int2ObjectMap<Map<PartPos, Integer>> observeTargetTickIntervals;\n    private final Int2ObjectMap<Map<PartPos, Integer>> observeTargetTicks;\n    private final Int2ObjectMap<Map<PrioritizedPartPos, IngredientCollectionDiffManager<T, M>>> channeledDiffManagers;\n    private final Int2ObjectMap<Set<PartPos>> pendingTickResets;\n\n    private final Int2ObjectMap<List<PrioritizedPartPos>> lastRemoved;\n    private final Map<PartPos, Integer> lastInventoryStates;\n    private Future<?> lastObserverBarrier;\n    private boolean runningObserverSync;\n    private boolean initialObservation;\n\n    public IngredientObserver(IPositionedAddonsNetworkIngredients<T, M> network) {\n        this.network = network;\n        this.worldProxy = new ConcurrentWorldIngredientsProxy<>(network);\n        this.changeObservers = Lists.newArrayList();\n        this.observeTargetTickIntervals = new Int2ObjectOpenHashMap<>();\n        this.observeTargetTicks = new Int2ObjectOpenHashMap<>();\n        this.channeledDiffManagers = new Int2ObjectOpenHashMap<>();\n        this.pendingTickResets = new Int2ObjectOpenHashMap<>();\n        this.lastRemoved = new Int2ObjectOpenHashMap<>();\n        this.lastInventoryStates = Maps.newHashMap();\n\n        this.lastObserverBarrier = null;\n        this.runningObserverSync = false;\n        this.initialObservation = true;\n    }\n\n    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {\n        return network;\n    }\n\n    @Nullable\n    public List<PrioritizedPartPos> getLastRemoved(int channel) {\n        return lastRemoved.get(channel);\n    }\n\n    public void onPositionRemoved(int channel, PrioritizedPartPos pos) {\n        List<PrioritizedPartPos> positions = this.lastRemoved.get(channel);\n        if (positions == null) {\n            positions = Lists.newLinkedList();\n            this.lastRemoved.put(channel, positions);\n        }\n        positions.add(pos);\n        this.lastInventoryStates.remove(pos.getPartPos());\n    }\n\n    /**\n     * Add an observer for listing to index change events.\n     * @param observer An index change observer.\n     */\n    public synchronized void addChangeObserver(IIngredientComponentStorageObservable.IIndexChangeObserver<T, M> observer) {\n        if (!changeObservers.contains(observer)) {\n            changeObservers.add(observer);\n        }\n    }\n\n    /**\n     * Remove the given index change observer.\n     * This will silently fail if the given observer was not registered.\n     * @param observer An index change observer.\n     */\n    public synchronized void removeChangeObserver(IIngredientComponentStorageObservable.IIndexChangeObserver<T, M> observer) {\n        changeObservers.remove(observer);\n    }\n\n    protected int getCurrentTick() {\n        return ServerLifecycleHooks.getCurrentServer().getTickCount();\n    }\n\n    protected void emitEvent(IIngredientComponentStorageObservable.StorageChangeEvent<T, M> event, boolean forceSync) {\n        if (GeneralConfig.ingredientNetworkObserverEnableMultithreading && !forceSync) {\n            // Make sure we are running on the main server thread to avoid concurrency exceptions\n            ServerLifecycleHooks.getCurrentServer().submitAsync(() -> {\n                for (IIngredientComponentStorageObservable.IIndexChangeObserver<T, M> observer : getObserversCopy()) {\n                    observer.onChange(event);\n                }\n            });\n        } else {\n            for (IIngredientComponentStorageObservable.IIndexChangeObserver<T, M> observer : getObserversCopy()) {\n                observer.onChange(event);\n            }\n        }\n    }\n\n    protected synchronized List<IIngredientComponentStorageObservable.IIndexChangeObserver<T, M>> getObserversCopy() {\n        return Lists.newArrayList(this.changeObservers);\n    }\n\n    protected int[] getChannels() {\n        int[] networkChannels = getNetwork().getChannels();\n        IntSet lastRemovedChannels = this.lastRemoved.keySet();\n        if (lastRemovedChannels.size() == 0) {\n            return networkChannels;\n        }\n        // We use a set that maintains insertion order,\n        // because we MUST iterate over the channels that have removals first!\n        IntSet uniqueChannels = new IntLinkedOpenHashSet();\n        for (int lastRemovedChannel : lastRemovedChannels) {\n            uniqueChannels.add(lastRemovedChannel);\n        }\n        for (int networkChannel : networkChannels) {\n            uniqueChannels.add(networkChannel);\n        }\n        return uniqueChannels.toIntArray();\n    }\n\n    /**\n     * @param forceSync If observation should happen synchronously.\n     * @return If an observation job was successfully started if it was needed.\n     */\n    protected boolean observe(boolean forceSync) {\n        if (!this.changeObservers.isEmpty()) {\n            // If we forcefully observe sync, make sure that no async observers are still running\n            if (forceSync && GeneralConfig.ingredientNetworkObserverEnableMultithreading\n                    && this.lastObserverBarrier != null && !this.lastObserverBarrier.isDone()) {\n                try {\n                    this.lastObserverBarrier.get();\n                } catch (InterruptedException | ExecutionException e) {\n                    e.printStackTrace();\n                }\n            }\n\n            if (GeneralConfig.ingredientNetworkObserverEnableMultithreading && !forceSync) {\n                // If we still have an uncompleted job (sync or async) from the previous tick, don't start a new one yet!\n                if ((this.lastObserverBarrier != null && !this.lastObserverBarrier.isDone()) || this.runningObserverSync) {\n                    return false;\n                }\n\n                // Run the world proxy in the world thread\n                this.worldProxy.onWorldTick();\n\n                // Schedule the observation job\n                this.lastObserverBarrier = WORKER_POOL.submit(() -> {\n                    for (int channel : getChannels()) {\n                        observe(channel, false);\n                    }\n                    this.initialObservation = false;\n                });\n            } else {\n                // If we have an uncompleted sync observer, don't start a new one yet!\n                if (this.runningObserverSync) {\n                    return false;\n                }\n\n                // Run the world proxy in the world thread\n                this.worldProxy.onWorldTick();\n\n                this.runningObserverSync = true;\n                for (int channel : getChannels()) {\n                    observe(channel, true);\n                }\n                this.runningObserverSync = false;\n                this.initialObservation = false;\n            }\n        }\n        return true;\n    }\n\n    protected synchronized Set<PrioritizedPartPos> getPositionsCopy(int channel) {\n        return Sets.newHashSet(getNetwork().getPrioritizedPositions(channel));\n    }\n\n    protected void observe(int channel, boolean forceSync) {\n        int currentTick = getCurrentTick();\n\n        // Prepare ticking collections\n        Map<PartPos, Integer> channelTargetTicks = observeTargetTicks.get(channel);\n        if (channelTargetTicks == null) {\n            channelTargetTicks = Maps.newHashMap();\n        }\n        Map<PartPos, Integer> channelIntervals = this.observeTargetTickIntervals.get(channel);\n        if (channelIntervals == null) {\n            channelIntervals = Maps.newHashMap();\n        }\n\n        // Calculate diff of all positions\n        Map<PrioritizedPartPos, IngredientCollectionDiffManager<T, M>> diffManagers = this.channeledDiffManagers.get(channel);\n        if (diffManagers == null) {\n            diffManagers = Maps.newHashMap();\n            this.channeledDiffManagers.put(channel, diffManagers);\n        }\n\n        // Check if we should diagnoze the observer\n        boolean isBeingDiagnozed = NetworkDiagnostics.getInstance().isBeingDiagnozed();\n        Map<PartPos, Long> lastSecondDurations = network.getLastSecondDurationIndex();\n        if (!isBeingDiagnozed && !lastSecondDurations.isEmpty()) {\n            // Make sure we aren't using any unnecessary memory.\n            lastSecondDurations.clear();\n        }\n\n        // Emit diffs for all current positions\n        Set<PrioritizedPartPos> positions = getPositionsCopy(channel);\n        for (PrioritizedPartPos partPos : positions) {\n            // Get current time if diagnostics are enabled\n            long startTime = 0;\n            if (isBeingDiagnozed) {\n                startTime = System.nanoTime();\n            }\n\n            // Check if we should observe this position in this tick\n            int lastTick = channelTargetTicks.getOrDefault(partPos.getPartPos(), currentTick);\n            if (lastTick <= currentTick) {\n                // Remove this position from the pending tick reset set\n                synchronized (this.pendingTickResets) {\n                    Set<PartPos> pendingTickResetsChannel = this.pendingTickResets.get(channel);\n                    if (pendingTickResetsChannel != null) {\n                        pendingTickResetsChannel.remove(partPos.getPartPos());\n                        if (pendingTickResetsChannel.isEmpty()) {\n                            this.pendingTickResets.remove(channel);\n                        }\n                    }\n                }\n\n                // If an inventory state is exposed, check if it has changed since the last observation call.\n                boolean skipPosition = false;\n\n                // Skip position forcefully if it is not loaded\n                if (!partPos.getPartPos().getPos().isLoaded()) {\n                    skipPosition = true;\n                }\n\n                if (!skipPosition) {\n                    Optional<Integer> newInventoryStateBoxed = this.worldProxy.getInventoryState(partPos.getPartPos());\n                    if (newInventoryStateBoxed.isPresent()) {\n                        Integer lastState = this.lastInventoryStates.get(partPos.getPartPos());\n                        int newState = newInventoryStateBoxed.get();\n                        if (lastState != null && lastState == newState) {\n                            // Skip this position if it hasn't not changed\n                            skipPosition = true;\n                        } else {\n                            this.lastInventoryStates.put(partPos.getPartPos(), newState);\n                        }\n                    }\n                }\n\n                if (!skipPosition) {\n                    IngredientCollectionDiffManager<T, M> diffManager = diffManagers.get(partPos);\n                    if (diffManager == null) {\n                        diffManager = new IngredientCollectionDiffManager<>(network.getComponent());\n                        diffManagers.put(partPos, diffManager);\n                    }\n\n                    // Emit event of diff\n                    Iterator<T> instances = this.worldProxy.getInstances(partPos.getPartPos()).iterator();\n                    IngredientCollectionDiff<T, M> diff = diffManager.onChange(instances);\n                    boolean hasChanges = false;\n                    if (diff.hasAdditions()) {\n                        hasChanges = true;\n                        this.emitEvent(new IIngredientComponentStorageObservable.StorageChangeEvent<>(channel, partPos,\n                                IIngredientComponentStorageObservable.Change.ADDITION, false, diff.getAdditions(), this.initialObservation), forceSync);\n                    }\n                    if (diff.hasDeletions()) {\n                        hasChanges = true;\n                        this.emitEvent(new IIngredientComponentStorageObservable.StorageChangeEvent<>(channel, partPos,\n                                IIngredientComponentStorageObservable.Change.DELETION, diff.isCompletelyEmpty(), diff.getDeletions(), this.initialObservation), forceSync);\n                    }\n\n                    // Update the next tick value\n                    int tickInterval = channelIntervals.getOrDefault(partPos.getPartPos(), GeneralConfig.ingredientNetworkObserverFrequencyMax);\n                    // Decrease the frequency when changes were detected\n                    // Increase the frequency when no changes were detected\n                    // This will make it so that quickly changing storages will be observed\n                    // more frequently than slowly changing storages\n                    boolean tickIntervalChanged = false;\n                    if (hasChanges) {\n                        if (tickInterval > GeneralConfig.ingredientNetworkObserverFrequencyMin) {\n                            tickIntervalChanged = true;\n                            tickInterval = Math.max(GeneralConfig.ingredientNetworkObserverFrequencyMin, tickInterval - GeneralConfig.ingredientNetworkObserverFrequencyDecreaseFactor);\n                        }\n                    } else {\n                        if (tickInterval < GeneralConfig.ingredientNetworkObserverFrequencyMax) {\n                            tickIntervalChanged = true;\n                            tickInterval = Math.min(GeneralConfig.ingredientNetworkObserverFrequencyMax, tickInterval + GeneralConfig.ingredientNetworkObserverFrequencyIncreaseFactor);\n                        }\n                    }\n                    // No need to store the interval if it == 1, as the previous or default value will\n                    // definitely also cause this part to tick in next tick.\n                    // This makes these cases slightly faster, as no map updates are needed.\n                    if (tickInterval != 1) {\n                        channelTargetTicks.put(partPos.getPartPos(), currentTick + tickInterval);\n\n                    }\n                    // Only update when the interval has changed.\n                    // In most cases, this will remain the same.\n                    if (tickIntervalChanged) {\n                        if (tickInterval != GeneralConfig.ingredientNetworkObserverFrequencyMax) {\n                            channelIntervals.put(partPos.getPartPos(), tickInterval);\n                        } else {\n                            channelIntervals.remove(partPos.getPartPos());\n                        }\n                    }\n                }\n            }\n\n            // Calculate duration if diagnostics are enabled\n            if (isBeingDiagnozed) {\n                long duration = System.nanoTime() - startTime;\n                PartPos interfacePos = PartTarget.fromCenter(partPos.getPartPos()).getTarget();\n                Long lastDuration = lastSecondDurations.get(interfacePos);\n                if (lastDuration != null) {\n                    duration = duration + lastDuration;\n                }\n                lastSecondDurations.put(interfacePos, duration);\n            }\n        }\n\n        // Emit deletions for all removed positions\n        List<PrioritizedPartPos> lastRemovedPositions = this.lastRemoved.get(channel);\n        if (lastRemovedPositions != null) {\n            for (PrioritizedPartPos partPos : lastRemovedPositions) {\n                IngredientCollectionDiffManager<T, M> diffManager = diffManagers.get(partPos);\n                if (diffManager != null) {\n                    // Emit event of diff with *empty* iterator\n                    IngredientCollectionDiff<T, M> diff = diffManager.onChange(Iterators.forArray());\n                    // No additions are possible\n                    if (diff.hasDeletions()) {\n                        this.emitEvent(new IIngredientComponentStorageObservable.StorageChangeEvent<>(channel, partPos,\n                                IIngredientComponentStorageObservable.Change.DELETION, diff.isCompletelyEmpty(), diff.getDeletions(), this.initialObservation), forceSync);\n                    }\n                }\n            }\n            this.lastRemoved.remove(channel);\n        }\n\n        // Store our new ticking collections\n        if (!channelTargetTicks.isEmpty()) {\n            observeTargetTicks.put(channel, channelTargetTicks);\n        }\n        if (!channelIntervals.isEmpty()) {\n            observeTargetTickIntervals.put(channel, channelIntervals);\n        }\n    }\n\n    public void resetTickInterval(int channel, PartPos targetPos) {\n        // Reset the world proxy\n        this.worldProxy.setRead(targetPos);\n\n        // Reset the channel ticks\n        Map<PartPos, Integer> channelTicks = this.observeTargetTicks.get(channel);\n        if (channelTicks == null) {\n            channelTicks = Maps.newHashMap();\n            this.observeTargetTicks.put(channel, channelTicks);\n        }\n        channelTicks.put(targetPos, getCurrentTick() + GeneralConfig.ingredientNetworkObserverFrequencyForced);\n\n        // Keep an overview of the pending positions per channel that require tick resets\n        synchronized (this.pendingTickResets) {\n            Set<PartPos> pendingTickResetsChannel = this.pendingTickResets.get(channel);\n            if (pendingTickResetsChannel == null) {\n                pendingTickResetsChannel = Sets.newHashSet();\n                this.pendingTickResets.put(channel, pendingTickResetsChannel);\n            }\n            pendingTickResetsChannel.add(targetPos);\n        }\n    }\n\n    public boolean isTickResetPending(int channel) {\n        synchronized (this.pendingTickResets) {\n            return this.pendingTickResets.containsKey(channel);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientPositionsIndex.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport it.unimi.dsi.fastutil.ints.AbstractInt2ObjectSortedMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;\nimport it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;\nimport org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.cyclopscore.datastructure.MultitransformIterator;\nimport org.cyclops.cyclopscore.ingredient.collection.IIngredientCollapsedCollectionMutable;\nimport org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;\nimport org.cyclops.cyclopscore.ingredient.collection.IngredientCollectionHelpers;\nimport org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientPositionsIndex;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\n\nimport java.util.Collection;\nimport java.util.Iterator;\n\n/**\n * An index that maps ingredients to positions that contain that instance.\n *\n * Positions are stored together with their priorities.\n * This makes it possible for instances of this class to guarantee that all returned iterators maintain priority order.\n *\n * @param <T> An instance type.\n * @param <M> The matching condition parameter.\n * @author rubensworks\n */\npublic class IngredientPositionsIndex<T, M> implements IIngredientPositionsIndex<T, M> {\n\n    private final IngredientComponent<T, M> component;\n    private final AbstractInt2ObjectSortedMap<IIngredientMapMutable<T, M, ObjectOpenHashSet<PartPos>>> prioritizedPositionsMap;\n    private final AbstractInt2ObjectSortedMap<IIngredientCollapsedCollectionMutable<T, M>> ingredientInstances;\n\n    public IngredientPositionsIndex(IngredientComponent<T, M> component) {\n        this.component = component;\n        this.prioritizedPositionsMap = new Int2ObjectAVLTreeMap<>();\n        this.ingredientInstances = new Int2ObjectAVLTreeMap<>();\n    }\n\n    protected T getPrototype(T instance) {\n        return this.getComponent().getMatcher().withQuantity(instance, 1);\n    }\n\n    protected int getInternalPriority(PrioritizedPartPos pos) {\n        // We need to do this as we want higher values to be returned first within the iterator\n        return -pos.getPriority();\n    }\n\n    @Override\n    public Iterator<PartPos> getNonEmptyPositions() {\n        return getPositions(getComponent().getMatcher().getEmptyInstance(), getComponent().getMatcher().getAnyMatchCondition());\n    }\n\n    @Override\n    public Iterator<PartPos> getPositions(T instance, M matchFlags) {\n        // Since we store ingredients by prototype in ingredientCollection,\n        // we can make the match flags more precise,\n        // and possibly improve performance of the lookup operation.\n        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();\n        if (matcher.getExactMatchNoQuantityCondition().equals(matchFlags)) {\n            matchFlags = matcher.getExactMatchCondition();\n        }\n        M finalMatchFlags = matchFlags;\n\n        return this.prioritizedPositionsMap.values()\n                .stream()\n                .flatMap(ingredientCollection -> ingredientCollection.getAll(getPrototype(instance), finalMatchFlags).stream())\n                .flatMap(Collection::stream)\n                .distinct()\n                .iterator();\n    }\n\n    @Override\n    public void addPosition(T instance, PrioritizedPartPos pos) {\n        IIngredientMapMutable<T, M, ObjectOpenHashSet<PartPos>> positionsMap = this.prioritizedPositionsMap.get(getInternalPriority(pos));\n        if (positionsMap == null) {\n            positionsMap = new IngredientHashMap<>(getComponent());\n            this.prioritizedPositionsMap.put(getInternalPriority(pos), positionsMap);\n        }\n\n        T prototype = getPrototype(instance);\n        ObjectOpenHashSet<PartPos> set = positionsMap.get(prototype);\n        if (set == null) {\n            set = new ObjectOpenHashSet<>();\n            positionsMap.put(prototype, set);\n        }\n\n        set.add(pos.getPartPos());\n    }\n\n    @Override\n    public void removePosition(T instance, PrioritizedPartPos pos) {\n        IIngredientMapMutable<T, M, ObjectOpenHashSet<PartPos>> positionsMap = this.prioritizedPositionsMap.get(getInternalPriority(pos));\n        if (positionsMap != null) {\n            T prototype = getPrototype(instance);\n            ObjectOpenHashSet<PartPos> set = positionsMap.get(prototype);\n            if (set != null) {\n                set.remove(pos.getPartPos());\n                if (set.isEmpty()) {\n                    positionsMap.remove(prototype);\n                    if (positionsMap.isEmpty()) {\n                        this.prioritizedPositionsMap.remove(getInternalPriority(pos));\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public long getQuantity(T instance) {\n        return this.ingredientInstances.values().stream()\n                .mapToLong(ingredients -> ingredients.getQuantity(instance))\n                .sum();\n    }\n\n    @Override\n    public IngredientComponent<T, M> getComponent() {\n        return this.component;\n    }\n\n    @Override\n    public int size() {\n        return this.ingredientInstances.values().stream()\n                .mapToInt(IIngredientCollapsedCollectionMutable::size)\n                .sum();\n    }\n\n    @Override\n    public boolean contains(T instance) {\n        return this.ingredientInstances.values().stream()\n                .anyMatch(ingredients -> ingredients.contains(instance));\n    }\n\n    @Override\n    public boolean contains(T instance, M matchCondition) {\n        return this.ingredientInstances.values().stream()\n                .anyMatch(ingredients -> ingredients.contains(instance, matchCondition));\n    }\n\n    @Override\n    public int count(T instance, M matchCondition) {\n        return this.ingredientInstances.values().stream()\n                .mapToInt(ingredients -> ingredients.count(instance, matchCondition))\n                .sum();\n    }\n\n    @Override\n    public Iterator<T> iterator(T instance, M matchCondition) {\n        return new MultitransformIterator<>(this.ingredientInstances.values().iterator(),\n                ingredients -> ingredients.iterator(instance, matchCondition));\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        return new MultitransformIterator<>(this.ingredientInstances.values().iterator(),\n                IIngredientCollapsedCollectionMutable::iterator);\n    }\n\n    public void removeAll(PrioritizedPartPos pos, Iterable<? extends T> instances) {\n        IIngredientCollapsedCollectionMutable<T, M> ingredients = this.ingredientInstances.get(getInternalPriority(pos));\n        if (ingredients != null) {\n            ingredients.removeAll(instances);\n            if (ingredients.isEmpty()) {\n                this.ingredientInstances.remove(getInternalPriority(pos));\n            }\n        }\n    }\n\n    public void addAll(PrioritizedPartPos pos, Iterable<? extends T> instances) {\n        IIngredientCollapsedCollectionMutable<T, M> ingredients = this.ingredientInstances.get(getInternalPriority(pos));\n        if (ingredients == null) {\n            ingredients = IngredientCollectionHelpers.createCollapsedCollection(component);\n            this.ingredientInstances.put(getInternalPriority(pos), ingredients);\n        }\n        ingredients.addAll(instances);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/IngredientPositionsIndexEmpty.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Iterators;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientPositionsIndex;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\n\nimport java.util.Iterator;\n\n/**\n * An empty ingredients positions index.\n * @param <T> An instance type.\n * @param <M> The matching condition parameter.\n * @author rubensworks\n */\npublic class IngredientPositionsIndexEmpty<T, M> implements IIngredientPositionsIndex<T, M> {\n\n    private final IngredientComponent<T, M> component;\n\n    public IngredientPositionsIndexEmpty(IngredientComponent<T, M> component) {\n        this.component = component;\n    }\n\n    @Override\n    public Iterator<PartPos> getNonEmptyPositions() {\n        return Iterators.forArray();\n    }\n\n    @Override\n    public Iterator<PartPos> getPositions(T instance, M matchFlags) {\n        return Iterators.forArray();\n    }\n\n    @Override\n    public void addPosition(T instance, PrioritizedPartPos pos) {\n\n    }\n\n    @Override\n    public void removePosition(T instance, PrioritizedPartPos pos) {\n\n    }\n\n    @Override\n    public long getQuantity(T instance) {\n        return 0;\n    }\n\n    @Override\n    public boolean contains(T instance) {\n        return false;\n    }\n\n    @Override\n    public boolean contains(T instance, M matchCondition) {\n        return false;\n    }\n\n    @Override\n    public int count(T instance, M matchCondition) {\n        return 0;\n    }\n\n    @Override\n    public IngredientComponent<T, M> getComponent() {\n        return this.component;\n    }\n\n    @Override\n    public int size() {\n        return 0;\n    }\n\n    @Override\n    public Iterator<T> iterator(T instance, M matchCondition) {\n        return Iterators.forArray();\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        return Iterators.forArray();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/Network.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap;\nimport it.unimi.dsi.fastutil.objects.Object2IntMap;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.fml.ModLoader;\nimport net.neoforged.neoforge.capabilities.ICapabilityProvider;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.network.*;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEventBus;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\nimport org.cyclops.integrateddynamics.capability.path.SidedPathElement;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;\nimport org.cyclops.integrateddynamics.core.network.event.NetworkElementAddEvent;\nimport org.cyclops.integrateddynamics.core.network.event.NetworkElementRemoveEvent;\nimport org.cyclops.integrateddynamics.core.network.event.NetworkEventBus;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.core.path.Cluster;\nimport org.cyclops.integrateddynamics.core.path.PathFinder;\nimport org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;\n\nimport java.util.*;\n\n/**\n * A network instance that can hold a set of {@link INetworkElement}s.\n * Note that this network only contains references to the relevant data, it does not contain the actual information.\n * @author rubensworks\n */\npublic class Network implements INetwork {\n\n    private Cluster baseCluster;\n\n    private final INetworkEventBus eventBus = new NetworkEventBus();\n    private final TreeSet<INetworkElement> elements = Sets.newTreeSet();\n    private Object2IntMap<INetworkElement> updateableElementsTicks = null;\n    private TreeSet<INetworkElement> invalidatedElements = Sets.newTreeSet();\n    private Map<INetworkElement, Long> lastSecondDurations = Maps.newHashMap();\n\n    private Map<NetworkCapability<?>, List<ICapabilityProvider<INetwork, Void, ?>>> capabilityProviders;\n    private IFullNetworkListener[] fullNetworkListeners;\n\n    private NetworkParams toRead = null;\n    private volatile boolean changed = false;\n    private volatile boolean killed = false;\n\n    private boolean crashed = false;\n\n    /**\n     * Initiate a full network from the given start position.\n     * @param sidedPathElement The sided path element to start from.\n     * @return The newly formed network.\n     */\n    public static Network initiateNetworkSetup(ISidedPathElement sidedPathElement) {\n        Network network = new Network(PathFinder.getConnectedCluster(sidedPathElement));\n        NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().addNewNetwork(network);\n        return network;\n    }\n\n    /**\n     * Check if two networks are equal.\n     * @param networkA A network.\n     * @param networkB Another network.\n     * @return If they are equal.\n     */\n    public static boolean areNetworksEqual(Network networkA, Network networkB) {\n        return networkA.elements.containsAll(networkB.elements) && networkA.elements.size() == networkB.elements.size();\n    }\n\n    /**\n     * This constructor should not be called, except for the process of constructing networks from NBT.\n     */\n    public Network() {\n        this.baseCluster = new Cluster();\n        gatherCapabilities();\n        onConstruct();\n    }\n\n    /**\n     * Create a new network from a given cluster of path elements.\n     * Each path element will be checked if it has a {@link INetworkElementProvider} capability at its position\n     * and will add all its elements to the network in that case.\n     * Each path element that has an {@link org.cyclops.integrateddynamics.api.part.IPartContainer} capability\n     * will have the network stored in its part container.\n     * @param pathElements The path elements that make up the connections in the network which can potentially provide network\n     *               elements.\n     */\n    public Network(Cluster pathElements) {\n        this.baseCluster = pathElements;\n        gatherCapabilities();\n        onConstruct();\n        deriveNetworkElements(baseCluster);\n    }\n\n    protected void gatherCapabilities() {\n        AttachCapabilitiesEventNetwork event = new AttachCapabilitiesEventNetwork(this);\n        ModLoader.postEventWrapContainerInModOrder(event);\n        List<IFullNetworkListener> listeners = event.getFullNetworkListeners();\n        this.fullNetworkListeners = listeners.toArray(new IFullNetworkListener[listeners.size()]);\n        this.capabilityProviders = event.getProviders();\n    }\n\n    protected IFullNetworkListener[] gatherFullNetworkListeners() {\n        List<IFullNetworkListener> listeners = Lists.newArrayList();\n\n        return listeners.toArray(new IFullNetworkListener[listeners.size()]);\n    }\n\n    protected void onConstruct() {\n\n    }\n\n    private void deriveNetworkElements(Cluster pathElements) {\n        if(!killIfEmpty()) {\n            for (ISidedPathElement sidedPathElement : pathElements) {\n                Level world = sidedPathElement.getPathElement().getPosition().getLevel(true);\n                BlockPos pos = sidedPathElement.getPathElement().getPosition().getBlockPos();\n                Direction side = sidedPathElement.getSide();\n                IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.NetworkCarrier.BLOCK).ifPresent(networkCarrier -> {\n                    // Correctly remove any previously saved network in this carrier\n                    // and set the new network to this.\n                    INetwork network = networkCarrier.getNetwork();\n                    if (network != null) {\n                        network.removePathElement(sidedPathElement.getPathElement(), side, world.getBlockState(pos), world.getBlockEntity(pos));\n                    }\n                    networkCarrier.setNetwork(null);\n                    networkCarrier.setNetwork(this);\n                });\n                IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.NetworkElementProvider.BLOCK).ifPresent(networkElementProvider -> {\n                    for(INetworkElement element : networkElementProvider.createNetworkElements(world, pos)) {\n                        addNetworkElement(element, true);\n                    }\n                });\n            }\n            onNetworkChanged();\n        }\n    }\n\n    @Override\n    public boolean isInitialized() {\n        return updateableElementsTicks != null;\n    }\n\n    @Override\n    public INetworkEventBus getEventBus() {\n        return this.eventBus;\n    }\n\n    /**\n     * Initialize the network element data.\n     */\n    public void initialize() {\n        initialize(false);\n    }\n\n    @Override\n    public boolean equals(Object object) {\n        return object instanceof Network && areNetworksEqual(this, (Network) object);\n    }\n\n    public NetworkParams toParams() {\n        return new NetworkParams(this.baseCluster.toParams(), this.crashed);\n    }\n\n    public void fromParams(NetworkParams params) {\n        // NBT reading is postponed until the first network tick, to ensure that the game is properly initialized.\n        // Because other mods may register things such as dimensions at the same time when networks\n        // are being constructed (as was the case in #349)\n        this.toRead = params;\n    }\n\n    public void fromParamsEffective(NetworkParams networkParams) {\n        this.baseCluster.fromParams(networkParams.pathElements());\n        this.crashed = networkParams.crashed();\n        deriveNetworkElements(baseCluster);\n        initialize(true);\n    }\n\n    @Override\n    public synchronized boolean addNetworkElement(INetworkElement element, boolean networkPreinit) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            if (!fullNetworkListener.addNetworkElement(element, networkPreinit)) {\n                return false;\n            }\n        }\n\n        if(getEventBus().postCancelable(new NetworkElementAddEvent.Pre(this, element))) {\n            elements.add(element);\n            if (!element.onNetworkAddition(this)) {\n                elements.remove(element);\n                return false;\n            }\n            if (!networkPreinit) {\n                addNetworkElementUpdateable(element);\n            }\n            if (element instanceof IEventListenableNetworkElement) {\n                IEventListenableNetworkElement<?> listenableElement = (IEventListenableNetworkElement<?>) element;\n                listenableElement.getNetworkEventListener().ifPresent(listener -> {\n                    if (listener.hasEventSubscriptions()) {\n                        for (Class<? extends INetworkEvent> eventType : listener.getSubscribedEvents()) {\n                            getEventBus().register(listenableElement, eventType);\n                        }\n                    }\n                });\n            }\n            getEventBus().post(new NetworkElementAddEvent.Post(this, element));\n            onNetworkChanged();\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public void addNetworkElementUpdateable(INetworkElement element) {\n        if(element.isUpdate()) {\n            updateableElementsTicks.put(element, 0);\n        }\n    }\n\n    @Override\n    public boolean removeNetworkElementPre(INetworkElement element) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            if (!fullNetworkListener.removeNetworkElementPre(element)) {\n                return false;\n            }\n        }\n        return getEventBus().postCancelable(new NetworkElementRemoveEvent.Pre(this, element));\n    }\n\n    @Override\n    public synchronized void setPriorityAndChannel(INetworkElement element, int priority, int channel) {\n        elements.remove(element);\n        int oldTickValue = updateableElementsTicks.defaultReturnValue();\n        if (element.isUpdate()) {\n            oldTickValue = updateableElementsTicks.removeInt(element);\n        }\n\n        //noinspection deprecation\n        element.setPriorityAndChannel(this, priority, channel);\n\n        elements.add(element);\n        if (element.isUpdate()) {\n            updateableElementsTicks.put(\n                element,\n                oldTickValue == updateableElementsTicks.defaultReturnValue()\n                    ? element.getUpdateInterval()\n                    : oldTickValue\n            );\n        }\n    }\n\n    @Override\n    public void removeNetworkElementPost(INetworkElement element, BlockState blockState, BlockEntity blockEntity) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.removeNetworkElementPost(element, blockState, blockEntity);\n        }\n        if (element instanceof IEventListenableNetworkElement) {\n            IEventListenableNetworkElement<?> listenableElement = (IEventListenableNetworkElement<?>) element;\n            listenableElement.getNetworkEventListener().ifPresent(listener -> {\n                if (listener.hasEventSubscriptions()) {\n                    getEventBus().unregister(listenableElement);\n                }\n            });\n        }\n        element.beforeNetworkKill(this, blockState, blockEntity);\n        element.onNetworkRemoval(this, blockState, blockEntity);\n        removeNetworkElementInternal(element);\n        getEventBus().post(new NetworkElementRemoveEvent.Post(this, element));\n        onNetworkChanged();\n    }\n\n    public void removeNetworkElementInternal(INetworkElement element) {\n        elements.remove(element);\n        removeNetworkElementUpdateable(element);\n        invalidatedElements.remove(element); // The element may be invalidated (like in an unloaded chunk) when it is being removed.\n    }\n\n    @Override\n    public synchronized void removeNetworkElementUpdateable(INetworkElement element) {\n        if (isInitialized()) {\n            updateableElementsTicks.removeInt(element);\n        }\n    }\n\n    /**\n     * Called when a network is server-loaded or newly created.\n     * @param silent If the element should not be notified for the network becoming alive.\n     */\n    protected void initialize(boolean silent) {\n        updateableElementsTicks = new Object2IntAVLTreeMap<>();\n        updateableElementsTicks.defaultReturnValue(Integer.MIN_VALUE);\n        for(INetworkElement element : elements) {\n            addNetworkElementUpdateable(element);\n            if(!silent) {\n                element.afterNetworkAlive(this);\n            }\n            element.afterNetworkReAlive(this);\n        }\n\n        // Once all elements are alive, send a single variable contents updated event.\n        this.getEventBus().post(new VariableContentsUpdatedEvent(this));\n    }\n\n    @Override\n    public void kill() {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.kill();\n        }\n        for(INetworkElement element : elements) {\n            element.beforeNetworkKill(this, null, null);\n        }\n        killed = true;\n    }\n\n    @Override\n    public boolean killIfEmpty() {\n        if(baseCluster.isEmpty()) {\n            kill();\n            onNetworkChanged();\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean canUpdate(INetworkElement element) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            if (!fullNetworkListener.canUpdate(element)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public void postUpdate(INetworkElement element) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.postUpdate(element);\n        }\n    }\n\n    @Override\n    public void onSkipUpdate(INetworkElement element) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.onSkipUpdate(element);\n        }\n    }\n\n    @Override\n    public void updateGuaranteed() {\n        if (this.toRead != null) {\n            this.fromParamsEffective(this.toRead);\n            this.toRead = null;\n        }\n    }\n\n    @Override\n    public final synchronized void update() {\n        this.changed = false;\n        if(killIfEmpty() || killed) {\n            NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().removeInvalidatedNetwork(this);\n        } else {\n            onUpdate();\n\n            // Update updateable network elements\n            boolean isBeingDiagnozed = NetworkDiagnostics.getInstance().isBeingDiagnozed();\n            if (!isBeingDiagnozed && !lastSecondDurations.isEmpty()) {\n                // Make sure we aren't using any unnecessary memory.\n                lastSecondDurations.clear();\n            }\n            for (Object2IntMap.Entry<INetworkElement> entry : updateableElementsTicks.object2IntEntrySet()) {\n                var element = entry.getKey();\n                try {\n                    if (isValid(element)) {\n                        long startTime = 0;\n                        if (isBeingDiagnozed) {\n                            startTime = System.nanoTime();\n                        }\n                        int lastElementTick = entry.getIntValue();\n                        if (canUpdate(element)) {\n                            if (lastElementTick <= 0) {\n                                entry.setValue(element.getUpdateInterval() - 1);\n                                element.update(this);\n                                postUpdate(element);\n                            } else {\n                                entry.setValue(lastElementTick - 1);\n                            }\n                        } else {\n                            onSkipUpdate(element);\n                            entry.setValue(lastElementTick - 1);\n                        }\n                        if (isBeingDiagnozed) {\n                            long duration = System.nanoTime() - startTime;\n                            Long lastDuration = lastSecondDurations.get(element);\n                            if (lastDuration != null) {\n                                duration = duration + lastDuration;\n                            }\n                            lastSecondDurations.put(element, duration);\n                        }\n                    }\n                } catch (PartStateException e) {\n                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, \"Attempted to tick a part that was not properly unloaded. \" +\n                            \"Report this to the Integrated Dynamics issue tracker with details on what you did \" +\n                            \"leading up to this stacktrace. The part was forcefully unloaded\");\n                    e.printStackTrace();\n                    element.invalidate(this);\n                }\n            }\n        }\n    }\n\n    protected void onUpdate() {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.update();\n        }\n    }\n\n    @Override\n    public synchronized boolean removePathElement(IPathElement pathElement, Direction side, BlockState blockState, BlockEntity blockEntity) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            if (!fullNetworkListener.removePathElement(pathElement, side, blockState, blockEntity)) {\n                return false;\n            }\n        }\n        if(baseCluster.remove(SidedPathElement.of(pathElement, null))) {\n            DimPos position = pathElement.getPosition();\n            Level level = position.getLevel(true);\n            INetworkElementProvider networkElementProvider = null;\n            if (level != null) {\n                networkElementProvider = level.getCapability(Capabilities.NetworkElementProvider.BLOCK, position.getBlockPos(), blockState, blockEntity, side);\n            }\n            if (networkElementProvider != null) {\n                Collection<INetworkElement> networkElements = networkElementProvider.\n                        createNetworkElements(position.getLevel(true), position.getBlockPos());\n                for (INetworkElement networkElement : networkElements) {\n                    if(!removeNetworkElementPre(networkElement)) {\n                        return false;\n                    }\n                }\n                for (INetworkElement networkElement : networkElements) {\n                    removeNetworkElementPost(networkElement, blockState, blockEntity);\n                }\n                onNetworkChanged();\n                return true;\n            }\n        } else {\n            Thread.dumpStack();\n            IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, \"Tried to remove a path element from a network it was not present in.\");\n            System.out.println(\"Cluster: \" + baseCluster);\n            System.out.println(\"Tried removing element: \" + pathElement);\n        }\n        return false;\n    }\n\n    @Override\n    public void afterServerLoad() {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.afterServerLoad();\n        }\n        // All networks start from an invalidated state at server start\n        for (INetworkElement element : getElements()) {\n            invalidateElement(element);\n        }\n\n    }\n\n    @Override\n    public void beforeServerStop() {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.beforeServerStop();\n        }\n    }\n\n    @Override\n    public Set<INetworkElement> getElements() {\n        return this.elements;\n    }\n\n    @Override\n    public boolean isKilled() {\n        return this.killed;\n    }\n\n    protected void onNetworkChanged() {\n        this.changed = true;\n    }\n\n    @Override\n    public boolean hasChanged() {\n        return this.changed;\n    }\n\n    @Override\n    public int getCablesCount() {\n        return baseCluster.size();\n    }\n\n    @Override\n    public long getLastSecondDuration(INetworkElement networkElement) {\n        Long duration = lastSecondDurations.get(networkElement);\n        return duration == null ? 0 : duration;\n    }\n\n    @Override\n    public void resetLastSecondDurations() {\n        lastSecondDurations.clear();\n    }\n\n    @Override\n    public boolean isCrashed() {\n        return crashed;\n    }\n\n    @Override\n    public void setCrashed(boolean crashed) {\n        this.crashed = crashed;\n    }\n\n    @Override\n    public <T> Optional<T> getCapability(NetworkCapability<T> capability) {\n        return Optional.ofNullable(capability.getCapability(this.capabilityProviders, this));\n    }\n\n    @Override\n    public void invalidateElement(INetworkElement element) {\n        if (element.canRevalidate(this)) {\n            // If this element could already be revalidated,\n            // this means that the element was not unloaded due to incorrect chunk unload,\n            // and therefore is corrupted, so let's forcefully remove it.\n            removeNetworkElementInternal(element);\n        } else {\n            for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n                fullNetworkListener.invalidateElement(element);\n            }\n            invalidatedElements.add(element);\n        }\n    }\n\n    @Override\n    public void revalidateElement(INetworkElement element) {\n        for (IFullNetworkListener fullNetworkListener : this.fullNetworkListeners) {\n            fullNetworkListener.revalidateElement(element);\n        }\n        invalidatedElements.remove(element);\n    }\n\n    @Override\n    public boolean containsSidedPathElement(ISidedPathElement pathElement) {\n        return baseCluster.contains(pathElement);\n    }\n\n    @Override\n    public IFullNetworkListener[] getFullNetworkListeners() {\n        return this.fullNetworkListeners;\n    }\n\n    @Override\n    public boolean isValid(INetworkElement element) {\n        if (!element.isLoaded()) {\n            return false;\n        }\n        if (invalidatedElements.contains(element)) {\n            if (element.canRevalidate(this)) {\n                element.revalidate(this);\n                return true;\n            }\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/NetworkCraftingHandlerRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.api.network.INetworkCraftingHandler;\nimport org.cyclops.integrateddynamics.api.network.INetworkCraftingHandlerRegistry;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * Registry for {@link INetworkCraftingHandler}.\n * @author rubensworks\n */\npublic final class NetworkCraftingHandlerRegistry implements INetworkCraftingHandlerRegistry {\n\n    private static NetworkCraftingHandlerRegistry INSTANCE = new NetworkCraftingHandlerRegistry();\n\n    private List<INetworkCraftingHandler> handlers = Lists.newArrayList();\n\n    private NetworkCraftingHandlerRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static NetworkCraftingHandlerRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <C extends INetworkCraftingHandler> C register(C craftingHandler) {\n        handlers.add(craftingHandler);\n        return craftingHandler;\n    }\n\n    @Override\n    public Collection<INetworkCraftingHandler> getCraftingHandlers() {\n        return handlers;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/NetworkElementBase.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * Base implementation for a network element.\n * @author rubensworks\n */\npublic abstract class NetworkElementBase implements INetworkElement {\n\n    @Override\n    public int getUpdateInterval() {\n        return 0;\n    }\n\n    @Override\n    public boolean isUpdate() {\n        return false;\n    }\n\n    @Override\n    public void update(INetwork network) {\n\n    }\n\n    @Override\n    public void beforeNetworkKill(INetwork network, @Nullable BlockState blockState, @Nullable BlockEntity blockEntity) {\n\n    }\n\n    @Override\n    public void afterNetworkAlive(INetwork network) {\n\n    }\n\n    @Override\n    public void afterNetworkReAlive(INetwork network) {\n\n    }\n\n    @Override\n    public void addDrops(BlockState blockState, BlockEntity blockEntity, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n\n    }\n\n    @Override\n    public boolean onNetworkAddition(INetwork network) {\n        return true;\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, BlockState blockState, BlockEntity blockEntity) {\n\n    }\n\n    @Override\n    public void onPreRemoved(INetwork network) {\n\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network) {\n\n    }\n\n    @Override\n    public void onNeighborBlockChange(@Nullable INetwork network, BlockGetter world, @Nullable Direction side) {\n\n    }\n\n    @Override\n    public void invalidate(INetwork network) {\n        network.invalidateElement(this);\n    }\n\n    @Override\n    public void revalidate(INetwork network) {\n        network.revalidateElement(this);\n    }\n\n    protected boolean canRevalidatePositioned(INetwork network, DimPos dimPos) {\n        return dimPos.isLoaded();\n    }\n\n    protected boolean revalidatePositioned(INetwork network, DimPos dimPos) {\n        return NetworkHelpers.getNetworkCarrier(dimPos.getLevel(true), dimPos.getBlockPos(), null)\n                .map(networkCarrier -> {\n                    if (networkCarrier.getNetwork() == null) {\n                        networkCarrier.setNetwork(network);\n                        return true;\n                    }\n                    return false;\n                })\n                .orElse(false);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/NetworkParams.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport org.cyclops.integrateddynamics.api.path.SidedPathElementParams;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic record NetworkParams(List<SidedPathElementParams> pathElements, boolean crashed) {\n    public static final Codec<NetworkParams> CODEC = RecordCodecBuilder.create(\n            builder -> builder.group(\n                            Codec.list(SidedPathElementParams.CODEC).fieldOf(\"pathElements\").forGetter(e -> e.pathElements),\n                            Codec.BOOL.fieldOf(\"crashed\").forGetter(e -> e.crashed)\n                    )\n                    .apply(builder, NetworkParams::new)\n    );\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PartNetwork.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Lists;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.datastructure.CompositeMap;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.block.IVariableContainer;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.network.FullNetworkListenerAdapter;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * A network that can hold parts.\n * Note that this network only contains references to the relevant data, it does not contain the actual information.\n * @author rubensworks\n */\npublic class PartNetwork extends FullNetworkListenerAdapter implements IPartNetwork {\n\n    private INetwork network;\n    private Int2ObjectMap<PartPos> partPositions = new Int2ObjectOpenHashMap<>();\n\n    public INetwork getNetwork() {\n        return network;\n    }\n\n    public void setNetwork(INetwork network) {\n        this.network = network;\n    }\n    private List<DimPos> variableContainerPositions = Lists.newArrayList();\n    private Map<Integer, IVariableFacade> compositeVariableCache = null;\n    private Int2ObjectMap<IValue> lazyExpressionValueCache = new Int2ObjectOpenHashMap<>();\n    private Int2ObjectMap<DimPos> proxyPositions = new Int2ObjectOpenHashMap<>();\n\n    private volatile boolean partsChanged = false;\n\n    @Override\n    public boolean addPart(int partId, PartPos partPos) {\n        if(partPositions.containsKey(partId)) {\n            return false;\n        }\n        compositeVariableCache = null;\n        partPositions.put(partId, partPos);\n        return true;\n    }\n\n    @Override\n    public IPartState getPartState(int partId) {\n        PartPos partPos = partPositions.get(partId);\n        return PartHelpers.getPartContainerChecked(partPos.getPos(), partPos.getSide()).getPartState(partPos.getSide());\n    }\n\n    @Override\n    public IPartType getPartType(int partId) {\n        PartPos partPos = partPositions.get(partId);\n        return PartHelpers.getPartContainerChecked(partPos.getPos(), partPos.getSide()).getPart(partPos.getSide());\n    }\n\n    @Override\n    public void removePart(int partId) {\n        compositeVariableCache = null;\n        partPositions.remove(partId);\n    }\n\n    @Override\n    public boolean hasPart(int partId) {\n        if(!partPositions.containsKey(partId)) {\n            return false;\n        }\n        PartPos partPos = partPositions.get(partId);\n        return PartHelpers.getPartContainer(partPos.getPos(), partPos.getSide())\n                .map(partContainer -> partContainer.hasPart(partPos.getSide()))\n                .orElse(false);\n    }\n\n    @Override\n    public <V extends IValue> boolean hasPartVariable(int partId, IAspectRead<V, ?> aspect) {\n        if(!hasPart(partId)) {\n            return false;\n        }\n        IPartState partState = getPartState(partId);\n        if(!(partState instanceof IPartStateReader)) {\n            return false;\n        }\n        IPartType partType = getPartType(partId);\n        if(!(partType instanceof IPartTypeReader)) {\n            return false;\n        }\n        try {\n            return ((IPartTypeReader) partType).getVariable(partType.getTarget(partPositions.get(partId), partState),\n                    (IPartStateReader) partState, aspect) != null;\n        } catch (IllegalArgumentException e) {\n            return false;\n        }\n    }\n\n    @Override\n    public <V extends IValue> IVariable<V> getPartVariable(int partId, IAspectRead<V, ?> aspect) {\n        return ((IPartStateReader) getPartState(partId)).getVariable(aspect);\n    }\n\n    protected Map<Integer, IVariableFacade> getVariableCache() {\n        if(compositeVariableCache == null) {\n            // Create a new composite map view on the existing variable containers in this network.\n            CompositeMap<Integer, IVariableFacade> compositeMap = new CompositeMap<>();\n            for(Iterator<DimPos> it = variableContainerPositions.iterator(); it.hasNext();) {\n                DimPos dimPos = it.next();\n                if (dimPos.isLoaded()) {\n                    IVariableContainer variableContainer = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos, null, Capabilities.VariableContainer.BLOCK).orElse(null);\n                    if (variableContainer != null) {\n                        compositeMap.addElement(variableContainer.getVariableCache());\n                    } else {\n                        IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, \"The variable container at \" + dimPos + \" was invalid, skipping.\");\n                        it.remove();\n                    }\n                }\n            }\n            // Also check parts\n            for(PartPos partPos : partPositions.values()) {\n                if (partPos.getPos().isLoaded()) {\n                    IPartContainer partContainer = PartHelpers.getPartContainerChecked(partPos.getPos(), partPos.getSide());\n                    partContainer.getCapability(Capabilities.VariableContainer.PART, network, this, PartTarget.fromCenter(partPos))\n                            .ifPresent(variableContainer -> compositeMap.addElement(variableContainer.getVariableCache()));\n                }\n            }\n            compositeVariableCache = compositeMap;\n        }\n        return compositeVariableCache;\n    }\n\n    @Override\n    public boolean hasVariableFacade(int variableId) {\n        return getVariableCache().containsKey(variableId);\n    }\n\n    @Override\n    public IVariableFacade getVariableFacade(int variableId) {\n        return getVariableCache().get(variableId);\n    }\n\n    @Override\n    public void setValue(int id, IValue value) {\n        lazyExpressionValueCache.put(id, value);\n    }\n\n    @Override\n    public boolean hasValue(int id) {\n        return lazyExpressionValueCache.containsKey(id);\n    }\n\n    @Override\n    public IValue getValue(int id) {\n        return lazyExpressionValueCache.get(id);\n    }\n\n    @Override\n    public void removeValue(int id) {\n        lazyExpressionValueCache.remove(id);\n    }\n\n    @Override\n    public boolean addVariableContainer(DimPos dimPos) {\n        compositeVariableCache = null;\n        return variableContainerPositions.add(dimPos);\n    }\n\n    @Override\n    public void removeVariableContainer(DimPos dimPos) {\n        compositeVariableCache = null;\n        variableContainerPositions.remove(dimPos);\n    }\n\n    @Override\n    public boolean addProxy(int proxyId, DimPos dimPos) {\n        if(proxyPositions.containsKey(proxyId)) {\n            return false;\n        }\n        proxyPositions.put(proxyId, dimPos);\n        return true;\n    }\n\n    @Override\n    public void removeProxy(int proxyId) {\n        proxyPositions.remove(proxyId);\n    }\n\n    @Override\n    public DimPos getProxy(int proxyId) {\n        return proxyPositions.get(proxyId);\n    }\n\n    @Override\n    public void notifyPartsChanged() {\n        this.partsChanged = true;\n    }\n\n    private void onPartsChanged() {\n\n    }\n\n    @Override\n    public void update() {\n        // Signal parts of any changes\n        if (partsChanged) {\n            this.partsChanged = false;\n            onPartsChanged();\n        }\n    }\n\n    @Override\n    public boolean removePathElement(IPathElement pathElement, Direction side, BlockState blockState, BlockEntity blockEntity) {\n        notifyPartsChanged();\n        return true;\n    }\n\n    @Override\n    public void invalidateElement(INetworkElement element) {\n        compositeVariableCache = null;\n        super.invalidateElement(element);\n    }\n\n    @Override\n    public void revalidateElement(INetworkElement element) {\n        compositeVariableCache = null;\n        super.revalidateElement(element);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PartNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.network.*;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * A network element for parts.\n * @author rubensworks\n */\npublic class PartNetworkElement<P extends IPartType<P, S>, S extends IPartState<P>> extends NetworkElementBase\n        implements IPartNetworkElement<P, S>, IEnergyConsumingNetworkElement {\n\n    private final P part;\n    private final PartPos center;\n\n    private S tempState = null;\n\n    public PartNetworkElement(P part, PartPos center) {\n        this.part = part;\n        this.center = center;\n    }\n\n    protected static DimPos getCenterPos(PartTarget target) {\n        return target.getCenter().getPos();\n    }\n\n    protected static Direction getCenterSide(PartTarget target) {\n        return target.getCenter().getSide();\n    }\n\n    protected static DimPos getTargetPos(PartTarget target) {\n        return target.getTarget().getPos();\n    }\n\n    protected static Direction getTargetSide(PartTarget target) {\n        return target.getTarget().getSide();\n    }\n\n    @Override\n    public P getPart() {\n        return part;\n    }\n\n    @Override\n    public PartTarget getTarget() {\n        return getPart().getTarget(this.center, getPartState());\n    }\n\n    public PartTarget getTarget(S partState) {\n        return getPart().getTarget(this.center, partState);\n    }\n\n    public S getTempState() {\n        return tempState;\n    }\n\n    public void setTempState(S tempState) {\n        this.tempState = tempState;\n    }\n\n    @Override\n    public IPartContainer getPartContainer() {\n        return PartHelpers.getPartContainerChecked(this.center.getPos(), this.center.getSide());\n    }\n\n    public IPartContainer getPartContainer(@Nullable BlockState blockState, @Nullable BlockEntity blockEntity) {\n        return PartHelpers.getPartContainerChecked(this.center.getPos(), this.center.getSide(), blockState, blockEntity);\n    }\n\n    public Optional<IPartContainer> getPartContainerOptional() {\n        return PartHelpers.getPartContainer(this.center.getPos(), this.center.getSide());\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n        //noinspection deprecation\n        part.setPriorityAndChannel(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), getPartState(), priority, channel);\n    }\n\n    @Override\n    public int getPriority() {\n        return hasPartState() ? part.getPriority(getPartState()) : 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return hasPartState() ? part.getChannel(getPartState()) : IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public boolean canRevalidate(INetwork network) {\n        return canRevalidatePositioned(network, this.center.getPos());\n    }\n\n    @Override\n    public void revalidate(INetwork network) {\n        super.revalidate(network);\n        if (revalidatePositioned(network, this.center.getPos())) {\n            afterNetworkReAlive(network);\n        }\n    }\n\n    @Override\n    public boolean isLoaded() {\n        return INetworkElement.shouldTick(this.center.getPos());\n    }\n\n    public boolean hasPartState() {\n        if (isLoaded()) {\n            return getPartContainerOptional()\n                    .map(partContainer -> partContainer.hasPart(this.center.getSide()))\n                    .orElse(false);\n        }\n        return false;\n    }\n\n    @Override\n    public S getPartState() throws PartStateException {\n        IPartContainer partContainer = getPartContainer();\n        if(partContainer != null) {\n            return (S) partContainer.getPartState(this.center.getSide());\n        } else {\n            throw new PartStateException(this.center.getPos(), this.center.getSide());\n        }\n    }\n\n    public S getPartState(@Nullable BlockState blockState, @Nullable BlockEntity blockEntity) throws PartStateException {\n        IPartContainer partContainer = getPartContainer(blockState, blockEntity);\n        if(partContainer != null) {\n            return (S) partContainer.getPartState(this.center.getSide());\n        } else {\n            throw new PartStateException(this.center.getPos(), this.center.getSide());\n        }\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        return getPart().getConsumptionRate(getPartState());\n    }\n\n    @Override\n    public void postUpdate(INetwork network, boolean updated) {\n        part.postUpdate(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), getPartState(), updated);\n    }\n\n    @Override\n    public int getUpdateInterval() {\n        return part.getUpdateInterval(getPartState());\n    }\n\n    @Override\n    public boolean isUpdate() {\n        return part.isUpdate(getPartState());\n    }\n\n    @Override\n    public void update(INetwork network) {\n        part.update(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), getPartState());\n    }\n\n    @Override\n    public void beforeNetworkKill(INetwork network, @Nullable BlockState blockState, @Nullable BlockEntity blockEntity) {\n        S partState = getPartState(blockState, blockEntity);\n        part.beforeNetworkKill(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(partState), partState);\n    }\n\n    @Override\n    public void afterNetworkAlive(INetwork network) {\n        part.afterNetworkAlive(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), getPartState());\n    }\n\n    @Override\n    public void afterNetworkReAlive(INetwork network) {\n        part.afterNetworkReAlive(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), getPartState());\n        this.center.getPos().getLevel(true).invalidateCapabilities(this.center.getPos().getBlockPos());\n    }\n\n    @Override\n    public void addDrops(BlockState blockState, BlockEntity blockEntity, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n        S partState = getPartState(blockState, blockEntity);\n        part.addDrops(getTarget(partState), partState, itemStacks, dropMainElement, saveState);\n    }\n\n    @Override\n    public boolean onNetworkAddition(INetwork network) {\n        IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n        boolean res = partNetwork.addPart(getPartState().getId(), this.center);\n        if(res) {\n            part.onNetworkAddition(network, partNetwork, getTarget(), getPartState());\n            this.center.getPos().getLevel(true).invalidateCapabilities(this.center.getPos().getBlockPos());\n        }\n        return res;\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, BlockState blockState, BlockEntity blockEntity) {\n        IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n        S partState = getPartState(blockState, blockEntity);\n        partNetwork.removePart(partState.getId());\n        part.onNetworkRemoval(network, partNetwork, getTarget(partState), partState);\n    }\n\n    @Override\n    public void onPreRemoved(INetwork network) {\n        part.onPreRemoved(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), (tempState = getPartState()));\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network) {\n        part.onPostRemoved(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(tempState), Objects.requireNonNull(tempState));\n        tempState = null;\n        this.center.getPos().getLevel(true).invalidateCapabilities(this.center.getPos().getBlockPos());\n    }\n\n    @Override\n    public void onNeighborBlockChange(@Nullable INetwork network, BlockGetter world, @Nullable Direction side) {\n        part.onBlockNeighborChange(network, NetworkHelpers.getPartNetworkChecked(network), getTarget(), getPartState(), world, side);\n    }\n\n    @Override\n    public Optional<P> getNetworkEventListener() {\n        return Optional.of(getPart());\n    }\n\n    public boolean equals(Object o) {\n        return o instanceof IPartNetworkElement && compareTo((INetworkElement) o) == 0;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = part.hashCode();\n        result = 31 * result + this.center.hashCode();\n        return result;\n    }\n\n    @Override\n    public int compareTo(INetworkElement o) {\n        if (o instanceof IPartNetworkElement) {\n            IPartNetworkElement p = (IPartNetworkElement) o;\n            int compClass = this.getPart().getUniqueName().compareTo(p.getPart().getUniqueName());\n            if (compClass == 0) {\n                // If this or the other part is not loaded, we IGNORE the priority,\n                // because that depends on tile entity data, which requires loading the part/chunk.\n                int compPriority = !isLoaded() || !p.isLoaded() ? 0 : -Integer.compare(this.getPriority(), p.getPriority());\n                if (compPriority == 0) {\n                    int compPart = getPart().getTranslationKey().compareTo(p.getPart().getTranslationKey());\n                    if (compPart == 0) {\n                        int compPos = this.center.getPos().compareTo(p.getPosition());\n                        if (compPos == 0) {\n                            return this.center.getSide().compareTo(p.getSide());\n                        }\n                        return compPos;\n                    }\n                    return compPart;\n                } else {\n                    return compPriority;\n                }\n            } else {\n                return compClass;\n            }\n        }\n\n        return this.getClass().getName().compareTo(o.getClass().getName());\n    }\n\n    @Override\n    public DimPos getPosition() {\n        return this.center.getPos();\n    }\n\n    @Override\n    public Direction getSide() {\n        return this.center.getSide();\n    }\n\n    @Override\n    public int getId() {\n        if (!hasPartState()) {\n            return -1;\n        }\n        return getPartState().getId();\n    }\n\n    @Override\n    public Identifier getGroup() {\n        return IPartNetworkElement.GROUP;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PartOffsetsClientNotifier.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.server.MinecraftServer;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.cyclops.integrateddynamics.network.packet.PartOffsetsDataPacket;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.UUID;\n\n/**\n * A server-side handler that sends updates to clients listening to part offset updates.\n * @author rubensworks\n */\npublic class PartOffsetsClientNotifier {\n\n    private static final PartOffsetsClientNotifier _INSTANCE = new PartOffsetsClientNotifier();\n\n    private final List<UUID> players = Lists.newArrayList();\n\n    private PartOffsetsClientNotifier() {\n\n    }\n\n    public static PartOffsetsClientNotifier getInstance() {\n        return _INSTANCE;\n    }\n\n    protected ServerPlayer getPlayer(UUID uuid) {\n        return ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayer(uuid);\n    }\n\n    public synchronized void registerPlayer(ServerPlayer player) {\n        if (!players.contains(player.getUUID())) {\n            players.add(player.getUUID());\n            sendPartOffsetsToPlayer(player);\n        }\n    }\n\n    public synchronized void unRegisterPlayer(ServerPlayer player) {\n        players.remove(player.getUUID());\n    }\n\n    public void sendPartOffsetsToPlayer(ServerPlayer player) {\n        BlockPos centerPos = player.getOnPos();\n        List<PartOffsetsClientNotifier.Entry> offsets = IModHelpers.get().getWorldHelpers().foldArea(player.level(), GeneralConfig.partOffsetRenderDistance, centerPos, (list, world, pos) -> {\n            IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityMultipartTicking.class)\n                    .ifPresent(blockEntity -> {\n                        for (Map.Entry<Direction, IPartType<?, ?>> entry : blockEntity.getPartContainer().getParts().entrySet()) {\n                            Direction sourceSide = entry.getKey();\n                            IPartType partType = entry.getValue();\n                            IPartState partState = blockEntity.getPartContainer().getPartState(entry.getKey());\n\n                            Vec3i targetOffset = partType.getTargetOffset(partState);\n                            Optional<Direction> targetSideOptional = Optional.ofNullable(partType.getTargetSideOverride(partState));\n                            if (!targetOffset.equals(Vec3i.ZERO) || targetSideOptional.isPresent()) {\n                                Direction targetSide = targetSideOptional.orElse(sourceSide.getOpposite());\n                                targetOffset = targetOffset.offset(-sourceSide.getOpposite().getStepX(), -sourceSide.getOpposite().getStepY(), -sourceSide.getOpposite().getStepZ());\n                                list.add(new PartOffsetsClientNotifier.Entry(pos, sourceSide, targetOffset, targetSide));\n                            }\n                        }\n                    });\n            return list;\n        }, Lists.newArrayList());\n\n        IntegratedDynamics._instance.getPacketHandler().sendToPlayer(new PartOffsetsDataPacket(offsets), player);\n    }\n\n    public void tick() {\n        MinecraftServer server = ServerLifecycleHooks.getCurrentServer();\n        for (UUID playerId : players) {\n            ServerPlayer player = server.getPlayerList().getPlayer(playerId);\n            if (player != null) {\n                sendPartOffsetsToPlayer(player);\n            }\n        }\n    }\n\n    public static record Entry(BlockPos source, Direction sourceSide, Vec3i targetOffset, Direction targetSide) {}\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PartPosIteratorHandlerDummy.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport java.util.Iterator;\nimport java.util.function.Supplier;\n\n/**\n * An {@link IPartPosIteratorHandler} that returns the given iterator unchanged.\n * @author rubensworks\n */\npublic class PartPosIteratorHandlerDummy implements IPartPosIteratorHandler {\n\n    public static final PartPosIteratorHandlerDummy INSTANCE = new PartPosIteratorHandlerDummy();\n\n    @Override\n    public Iterator<PartPos> handleIterator(Supplier<Iterator<PartPos>> iteratorSupplier, int channel) {\n        return iteratorSupplier.get();\n    }\n\n    @Override\n    public IPartPosIteratorHandler clone() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PartPosIteratorHandlerRoundRobin.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Iterators;\nimport org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\n\nimport java.util.Iterator;\nimport java.util.function.Supplier;\n\n/**\n * An {@link IPartPosIteratorHandler} that maintains the state of an iterator\n * and achieves round-robin iteration.\n * @author rubensworks\n */\npublic class PartPosIteratorHandlerRoundRobin implements IPartPosIteratorHandler {\n\n    private int offset;\n\n    public PartPosIteratorHandlerRoundRobin(int offset) {\n        this.offset = offset;\n    }\n\n    public PartPosIteratorHandlerRoundRobin() {\n        this(0);\n    }\n\n    @Override\n    public Iterator<PartPos> handleIterator(Supplier<Iterator<PartPos>> iteratorSupplier, int channel) {\n        Iterator<PartPos> it = iteratorSupplier.get();\n\n        // Offset the iterator\n        int advanced = Iterators.advance(it, offset);\n        if (offset >= advanced && !it.hasNext()) {\n            // Restart the iterator if the offset reached the end.\n            offset = Math.max(0, offset - advanced);\n            it = iteratorSupplier.get();\n        }\n\n        // Wrap the iterator in an iterator that will increase the offset of this handler for each `next` call.\n        // If the initial offset was >0, it will allow the iterator to be reset\n        // and iterate over the reset iterator `offset` times.\n        return new CountingIterator(this, it, offset, iteratorSupplier);\n    }\n\n    @Override\n    public IPartPosIteratorHandler clone() {\n        return new PartPosIteratorHandlerRoundRobin(this.offset);\n    }\n\n    public static class CountingIterator implements Iterator<PartPos> {\n\n        private final PartPosIteratorHandlerRoundRobin handler;\n        private Iterator<PartPos> innerIt;\n        private final Supplier<Iterator<PartPos>> iteratorSupplier;\n\n        private boolean countdownAllowedRemaining;\n        private int allowedRemaining;\n\n        public CountingIterator(PartPosIteratorHandlerRoundRobin handler, Iterator<PartPos> innerIt,\n                                int initialOffset, Supplier<Iterator<PartPos>> iteratorSupplier) {\n            this.handler = handler;\n            this.innerIt = innerIt;\n            this.iteratorSupplier = iteratorSupplier;\n\n            this.countdownAllowedRemaining = false;\n            this.allowedRemaining = initialOffset;\n        }\n\n        @Override\n        public boolean hasNext() {\n            if (innerIt.hasNext()) {\n                return true;\n            } else if (!countdownAllowedRemaining) {\n                countdownAllowedRemaining = true;\n                handler.offset = 0;\n                if (allowedRemaining > 0) {\n                    innerIt = iteratorSupplier.get();\n                    return innerIt.hasNext();\n                }\n            }\n            return false;\n        }\n\n        @Override\n        public PartPos next() {\n            PartPos next = innerIt.next();\n\n            // Handle offset\n            handler.offset++;\n            if (countdownAllowedRemaining) {\n                allowedRemaining--;\n                if (allowedRemaining <= 0) {\n                    // Set to empty iterator if we are not allowed to progress anymore.\n                    innerIt = Iterators.forArray();\n                }\n            }\n\n            return next;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PositionedAddonsNetwork.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\n\n/**\n * A network that can hold prioritized positions.\n * @author rubensworks\n */\npublic abstract class PositionedAddonsNetwork implements IPositionedAddonsNetwork {\n\n    private INetwork network;\n    private final Set<PrioritizedPartPos> allPositions = Sets.newTreeSet();\n\n    public INetwork getNetwork() {\n        return network;\n    }\n\n    public void setNetwork(INetwork network) {\n        this.network = network;\n    }\n    private final Int2ObjectMap<Set<PrioritizedPartPos>> positions = new Int2ObjectOpenHashMap<>();\n    private final Map<PartPos, Integer> positionChannels = Maps.newHashMap();\n    // We store the thread id together with the disabled position.\n    // This is to make sure that different threads can safely iterate over positions in parallel\n    // without clashing with each other, as this could lead to problems such as in #194.\n    // This for example applies to the ingredient observer and in-world ingredient movement.\n    private final Set<Pair<Long, PartPos>> disabledPositions = Sets.newHashSet();\n\n    private IPartPosIteratorHandler partPosIteratorHandler = null;\n\n    @Override\n    public int[] getChannels() {\n        return positions.keySet().toIntArray();\n    }\n\n    @Override\n    public boolean hasPositions() {\n        return !positions.isEmpty();\n    }\n\n    @Override\n    public Collection<PrioritizedPartPos> getPrioritizedPositions(int channel) {\n        if (channel == WILDCARD_CHANNEL) {\n            return getPrioritizedPositions();\n        }\n        Set<PrioritizedPartPos> positions = this.positions.get(channel);\n        Set<PrioritizedPartPos> wildcardPositions = this.positions.get(WILDCARD_CHANNEL);\n        if (positions == null) {\n            if (wildcardPositions != null) {\n                return wildcardPositions;\n            }\n            positions = Collections.emptySet();\n        }\n        if (wildcardPositions == null) {\n            return positions;\n        }\n        TreeSet<PrioritizedPartPos> merged = Sets.newTreeSet();\n        merged.addAll(positions);\n        merged.addAll(wildcardPositions);\n        return merged;\n    }\n\n    @Override\n    public Collection<PrioritizedPartPos> getPrioritizedPositions() {\n        return this.allPositions;\n    }\n\n    @Override\n    public int getPositionChannel(PartPos pos) {\n        return this.positionChannels.getOrDefault(pos, -1);\n    }\n\n    protected void invalidateIterators() {\n        setPartPosIteratorHandler(null);\n    }\n\n    @Override\n    public void setPartPosIteratorHandler(@Nullable IPartPosIteratorHandler iteratorHandler) {\n        this.partPosIteratorHandler = iteratorHandler;\n    }\n\n    @Nullable\n    @Override\n    public IPartPosIteratorHandler getPartPosIteratorHandler() {\n        if (partPosIteratorHandler != null) {\n            return partPosIteratorHandler;\n        }\n        return null;\n    }\n\n    @Override\n    public boolean addPosition(PartPos pos, int priority, int channel) {\n        invalidateIterators();\n\n        PrioritizedPartPos prioritizedPosition = PrioritizedPartPos.of(pos, priority);\n        if (allPositions.add(prioritizedPosition)) {\n            Set<PrioritizedPartPos> positions = this.positions.get(channel);\n            if (positions == null) {\n                positions = Sets.newTreeSet();\n                this.positions.put(channel, positions);\n            }\n            positions.add(prioritizedPosition);\n            this.positionChannels.put(pos, channel);\n            this.onPositionAdded(channel, prioritizedPosition);\n            return true;\n        }\n        return false;\n    }\n\n    protected void onPositionAdded(int channel, PrioritizedPartPos pos) {\n\n    }\n\n    @Override\n    public void removePosition(PartPos pos) {\n        invalidateIterators();\n\n        Wrapper<Integer> removedChannel = new Wrapper<>(-2);\n        Wrapper<PrioritizedPartPos> removedPos = new Wrapper<>(null);\n        for (Int2ObjectMap.Entry<Set<PrioritizedPartPos>> entry : this.positions.int2ObjectEntrySet()) {\n            int channel = entry.getIntKey();\n            Set<PrioritizedPartPos> positions = entry.getValue();\n            Iterator<PrioritizedPartPos> it = positions.iterator();\n            while (it.hasNext()) {\n                PrioritizedPartPos prioritizedPartPos = it.next();\n                if (prioritizedPartPos.getPartPos().equals(pos)) {\n                    it.remove();\n                    allPositions.remove(prioritizedPartPos);\n                    removedPos.set(prioritizedPartPos);\n                    removedChannel.set(channel);\n                    break;\n                }\n            }\n        }\n        int channel = removedChannel.get();\n        if (channel != -2) {\n            this.onPositionRemoved(channel, removedPos.get());\n            if (positions.get(channel).isEmpty()) {\n                this.positions.remove(channel);\n            }\n        }\n        positionChannels.remove(pos);\n    }\n\n    protected void onPositionRemoved(int channel, PrioritizedPartPos pos) {\n\n    }\n\n    @Override\n    public boolean isPositionDisabled(PartPos pos) {\n        return disabledPositions.contains(Pair.of(Thread.currentThread().getId(), pos));\n    }\n\n    @Override\n    public void disablePosition(PartPos pos) {\n        disabledPositions.add(Pair.of(Thread.currentThread().getId(), pos));\n    }\n\n    @Override\n    public void enablePosition(PartPos pos) {\n        disabledPositions.remove(Pair.of(Thread.currentThread().getId(), pos));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/PositionedAddonsNetworkIngredients.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;\nimport it.unimi.dsi.fastutil.ints.Int2IntMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.capabilities.BlockCapability;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageSlotted;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageWrapperHandler;\nimport org.cyclops.commoncapabilities.api.ingredient.storage.IngredientComponentStorageEmpty;\nimport org.cyclops.cyclopscore.ingredient.collection.IIngredientCollection;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientComponentStorageObservable;\nimport org.cyclops.integrateddynamics.api.ingredient.IIngredientPositionsIndex;\nimport org.cyclops.integrateddynamics.api.network.*;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\n\n/**\n * An ingredient network that can hold prioritized positions.\n * @param <T> The instance type.\n * @param <M> The matching condition parameter, may be Void. Instances MUST properly implement the equals method.\n * @author rubensworks\n */\npublic abstract class PositionedAddonsNetworkIngredients<T, M> extends PositionedAddonsNetwork\n        implements IPositionedAddonsNetworkIngredients<T, M>, IFullNetworkListener,\n        IIngredientComponentStorageObservable.IIndexChangeObserver<T, M> {\n\n    private final IngredientComponent<T, M> component;\n\n    private final IngredientObserver<T, M> ingredientObserver;\n    private final Int2ObjectMap<IngredientPositionsIndex<T, M>> indexes;\n    private final Map<PartPos, PositionedAddonsNetworkIngredientsFilter<T>> positionFilters = Maps.newHashMap();\n    private final LoadingCache<PartPos, IIngredientComponentStorage<T, M>> cacheStorage;\n    private final Int2IntMap cacheChannelSlots;\n    private final Set<IIngredientChannelInsertPreConsumer<T>> insertPreConsumers;\n\n    private boolean observe;\n    private Map<PartPos, Long> lastSecondDurations = Maps.newHashMap();\n\n    public PositionedAddonsNetworkIngredients(IngredientComponent<T, M> component) {\n        this.component = component;\n\n        this.ingredientObserver = new IngredientObserver<>(this);\n        this.ingredientObserver.addChangeObserver(this);\n        this.indexes = new Int2ObjectOpenHashMap<>();\n        // This cache is invalidated after every tick\n        this.cacheStorage = CacheBuilder.newBuilder()\n                .build(new CacheLoader<PartPos, IIngredientComponentStorage<T, M>>() {\n            public IIngredientComponentStorage<T, M> load(PartPos pos) {\n                IIngredientComponentStorage<T, M> storage = getPositionedStorageUnsafe(pos);\n                return storage == null ? new IngredientComponentStorageEmpty<>(getComponent()) : storage;\n            }\n        });\n        this.cacheChannelSlots = new Int2IntLinkedOpenHashMap();\n        this.insertPreConsumers = Sets.newIdentityHashSet();\n\n        this.observe = false;\n    }\n\n    @Override\n    public IngredientComponent<T, M> getComponent() {\n        return component;\n    }\n\n    @Override\n    public IIngredientComponentStorage<T, M> getPositionedStorage(PartPos pos) {\n        try {\n            return this.cacheStorage.get(pos);\n        } catch (ExecutionException e) {\n            return new IngredientComponentStorageEmpty<>(getComponent());\n        }\n    }\n\n    @Nullable\n    public IIngredientPositionsIndex<T, M> getInstanceLocationsIndex(int channel) {\n        return this.indexes.get(channel);\n    }\n\n    @Override\n    public boolean addPosition(PartPos pos, int priority, int channel) {\n        return getPositionedStorageUnsafe(pos) != null && super.addPosition(pos, priority, channel);\n    }\n\n    @Override\n    public void setPositionedStorageFilter(PartPos pos, @Nullable PositionedAddonsNetworkIngredientsFilter<T> filter) {\n        if (filter == null) {\n            positionFilters.remove(pos);\n        } else {\n            positionFilters.put(pos, filter);\n        }\n    }\n\n    @Nullable\n    @Override\n    public PositionedAddonsNetworkIngredientsFilter<T> getPositionedStorageFilter(PartPos pos) {\n        return positionFilters.get(pos);\n    }\n\n    @Override\n    public void onChange(IIngredientComponentStorageObservable.StorageChangeEvent<T, M> event) {\n        applyChangesToChannel(event, event.getChannel());\n        applyChangesToChannel(event, -1); // Apply all changes to \"all\" channels\n\n        if (GeneralConfig.logChangeEvents) {\n            System.out.println(this.toString() + event);\n        }\n    }\n\n    protected void applyChangesToChannel(IIngredientComponentStorageObservable.StorageChangeEvent<T, M> event, int channel) {\n        IIngredientCollection<T, M> instances = event.getInstances();\n        PrioritizedPartPos pos = event.getPos();\n        IngredientPositionsIndex<T, M> index = getIndexSafe(channel);\n        if (event.getChangeType() == IIngredientComponentStorageObservable.Change.DELETION) {\n            index.removeAll(pos, instances);\n            if (event.isCompleteChange()) {\n                for (T instance : instances) {\n                    index.removePosition(instance, pos);\n                }\n            }\n\n            // Cleanup empty collections\n            if (index.isEmpty()) {\n                this.indexes.remove(channel);\n            }\n        } else if (event.getChangeType() == IIngredientComponentStorageObservable.Change.ADDITION) {\n            index.addAll(pos, instances);\n            for (T instance : instances) {\n                index.addPosition(instance, pos);\n            }\n        }\n    }\n\n    protected IngredientPositionsIndex<T, M> getIndexSafe(int channel) {\n        IngredientPositionsIndex<T, M> index = this.indexes.get(channel);\n        if (index == null) {\n            index = new IngredientPositionsIndex<>(getComponent());\n            this.indexes.put(channel, index);\n        }\n        return index;\n    }\n\n    @Override\n    protected void onPositionAdded(int channel, PrioritizedPartPos pos) {\n        super.onPositionAdded(channel, pos);\n\n        // If our position was added to the lastRemoved list without it being processed yet,\n        // remove it from the list before that processing is going to start.\n        List<PrioritizedPartPos> lastRemoved = ingredientObserver.getLastRemoved(channel);\n        if (lastRemoved != null) {\n            lastRemoved.remove(pos);\n        }\n    }\n\n    @Override\n    protected void onPositionRemoved(int channel, PrioritizedPartPos pos) {\n        super.onPositionRemoved(channel, pos);\n        ingredientObserver.onPositionRemoved(channel, pos);\n    }\n\n    @Override\n    public INetworkIngredientsChannel<T, M> getChannel(int channel) {\n        return new IngredientChannelIndexed<>(this, channel, getChannelIndex(channel));\n    }\n\n    @Override\n    public void addObserver(IIndexChangeObserver<T, M> observer) {\n        this.ingredientObserver.addChangeObserver(observer);\n    }\n\n    @Override\n    public void removeObserver(IIndexChangeObserver<T, M> observer) {\n        this.ingredientObserver.removeChangeObserver(observer);\n    }\n\n    @Override\n    public void scheduleObservation() {\n        this.observe = true;\n    }\n\n    @Override\n    public void scheduleObservationForced(int channel, PartPos pos) {\n        scheduleObservation();\n        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {\n            this.ingredientObserver.resetTickInterval(getPositionChannel(pos), pos);\n        } else {\n            this.ingredientObserver.resetTickInterval(channel, pos);\n        }\n    }\n\n    @Override\n    public boolean shouldObserve() {\n        return this.observe;\n    }\n\n    @Override\n    public boolean isObservationForcedPending(int channel) {\n        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {\n            for (int channelActual : getChannels()) {\n                if (this.ingredientObserver.isTickResetPending(channelActual)) {\n                    return true;\n                }\n            }\n            return false;\n        } else {\n            return this.ingredientObserver.isTickResetPending(channel);\n        }\n    }\n\n    @Override\n    public void runObserverSync() {\n        if (this.ingredientObserver.observe(true)) {\n            this.observe = false;\n        }\n    }\n\n    @Override\n    public IIngredientPositionsIndex<T, M> getChannelIndex(int channel) {\n        IIngredientPositionsIndex<T, M> index = getInstanceLocationsIndex(channel);\n        if (index == null) {\n            // This can occur when the index is empty,\n            // which can be caused by all attached storages being empty or no storages being available.\n            index = new IngredientPositionsIndexEmpty<>(getComponent());\n        }\n        return index;\n    }\n\n    @Override\n    public IIngredientComponentStorageSlotted<T, M> getChannelSlotted(int channel) {\n        return new IngredientChannelAdapterWrapperSlotted<>(\n                (IngredientChannelAdapter<T, M>) getChannel(channel), this.cacheChannelSlots);\n    }\n\n    @Nullable\n    @Override\n    public <S, C> S getChannelExternal(BlockCapability<S, C> capability, int channel) {\n        IIngredientComponentStorageWrapperHandler<T, M, S, C> wrapperHandler = getComponent()\n                .getStorageWrapperHandler(capability);\n        return wrapperHandler != null ? wrapperHandler.wrapStorage(getChannelSlotted(channel)) : null;\n    }\n\n    @Override\n    public boolean addNetworkElement(INetworkElement element, boolean networkPreinit) {\n        return true;\n    }\n\n    @Override\n    public boolean removeNetworkElementPre(INetworkElement element) {\n        return true;\n    }\n\n    @Override\n    public void removeNetworkElementPost(INetworkElement element, BlockState blockState, BlockEntity blockEntity) {\n\n    }\n\n    @Override\n    public void kill() {\n\n    }\n\n    @Override\n    public void updateGuaranteed() {\n\n    }\n\n    @Override\n    public void update() {\n        if (this.shouldObserve()) {\n            if (this.ingredientObserver.observe(false)) {\n                this.observe = false;\n            }\n        }\n\n        // Clear caches after each tick\n        this.cacheStorage.invalidateAll();\n        this.cacheChannelSlots.clear();\n    }\n\n    @Override\n    public boolean removePathElement(IPathElement pathElement, Direction side, BlockState blockState, BlockEntity blockEntity) {\n        return true;\n    }\n\n    @Override\n    public void afterServerLoad() {\n\n    }\n\n    @Override\n    public void beforeServerStop() {\n\n    }\n\n    @Override\n    public boolean canUpdate(INetworkElement element) {\n        return true;\n    }\n\n    @Override\n    public void onSkipUpdate(INetworkElement element) {\n\n    }\n\n    @Override\n    public void postUpdate(INetworkElement element) {\n\n    }\n\n    @Override\n    public Map<PartPos, Long> getLastSecondDurationIndex() {\n        return lastSecondDurations;\n    }\n\n    @Override\n    public void resetLastSecondDurationsIndex() {\n        lastSecondDurations.clear();\n    }\n\n    @Override\n    public void invalidateElement(INetworkElement element) {\n\n    }\n\n    @Override\n    public void revalidateElement(INetworkElement element) {\n\n    }\n\n    @Override\n    public void registerInsertPreConsumer(IIngredientChannelInsertPreConsumer<T> preConsumer) {\n        insertPreConsumers.add(preConsumer);\n    }\n\n    @Override\n    public void unregisterInsertPreConsumer(IIngredientChannelInsertPreConsumer<T> preConsumer) {\n        insertPreConsumers.remove(preConsumer);\n    }\n\n    @Override\n    public Set<IIngredientChannelInsertPreConsumer<T>> getInsertPreConsumers() {\n        return insertPreConsumers;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/TileNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.core.network;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPositionedNetworkElement;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityCableConnectableInventory;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Network element for part entities.\n * @author rubensworks\n */\npublic abstract class TileNetworkElement<T extends BlockEntityCableConnectableInventory> extends ConsumingNetworkElementBase\n        implements IPositionedNetworkElement {\n\n    private final DimPos pos;\n\n    public TileNetworkElement(DimPos pos) {\n        this.pos = pos;\n    }\n\n    public DimPos getPos() {\n        return pos;\n    }\n\n    protected abstract Class<T> getTileClass();\n\n    protected Optional<T> getTile() {\n        return IModHelpers.get().getBlockEntityHelpers().get(getPos().getLevel(true), getPos().getBlockPos(), getTileClass());\n    }\n\n    @Override\n    public int compareTo(INetworkElement o) {\n        if(o instanceof TileNetworkElement) {\n            return getPos().compareTo(((TileNetworkElement) o).getPos());\n        }\n        return this.getClass().getCanonicalName().compareTo(o.getClass().getCanonicalName());\n    }\n\n    @Override\n    public void afterNetworkReAlive(INetwork network) {\n        super.afterNetworkReAlive(network);\n        getTile().ifPresent(T::afterNetworkReAlive);\n    }\n\n    @Override\n    public boolean canRevalidate(INetwork network) {\n        return canRevalidatePositioned(network, pos);\n    }\n\n    @Override\n    public void revalidate(INetwork network) {\n        super.revalidate(network);\n        revalidatePositioned(network, pos);\n    }\n\n    @Override\n    public DimPos getPosition() {\n        return this.pos;\n    }\n\n    @Override\n    public boolean isLoaded() {\n        return INetworkElement.shouldTick(this.getPos());\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        TileNetworkElement<?> that = (TileNetworkElement<?>) o;\n        return Objects.equals(pos, that.pos);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hashCode(pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/IRawData.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\n/**\n * @author rubensworks\n */\npublic interface IRawData {\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/NetworkDataClient.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\nimport com.google.common.collect.ArrayListMultimap;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Multimap;\nimport com.google.common.collect.Sets;\nimport com.google.gson.*;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.network.packet.PlayerTeleportPacket;\n\nimport java.util.*;\n\n/**\n * @author rubensworks\n */\npublic class NetworkDataClient {\n\n    public static final String[] LOCALIZE_ENTRIES = new String[]{\n            \"gui.integrateddynamics.diagnostics.title\",\n            \"gui.integrateddynamics.diagnostics.parts\",\n            \"gui.integrateddynamics.diagnostics.observers\",\n            \"gui.integrateddynamics.diagnostics.table.network\",\n            \"gui.integrateddynamics.diagnostics.table.cables\",\n            \"gui.integrateddynamics.diagnostics.table.part\",\n            \"gui.integrateddynamics.diagnostics.table.ticktime\",\n            \"gui.integrateddynamics.diagnostics.table.dimension\",\n            \"gui.integrateddynamics.diagnostics.table.position\",\n            \"gui.integrateddynamics.diagnostics.table.side\",\n            \"gui.integrateddynamics.diagnostics.table.actions\"\n    };\n\n    private static final Multimap<Integer, ObservablePartData> networkDataParts = ArrayListMultimap.create();\n    private static final Multimap<Integer, ObservableObserverData> networkDataObservers = ArrayListMultimap.create();\n\n    public static void clearNetworkData() {\n        networkDataParts.clear();\n        networkDataObservers.clear();\n    }\n\n    public static void setNetworkData(int id, RawNetworkData rawNetworkData) {\n        synchronized (networkDataParts) {\n            Collection<ObservablePartData> previous = networkDataParts.removeAll(id);\n\n            // The positions that were being rendered previously\n            Set<PartPos> previousPositionsWithRender = Sets.newHashSet();\n            for (ObservablePartData partData : previous) {\n                PartPos pos = partData.toPartPos();\n                if (pos != null && NetworkDiagnosticsPartOverlayRenderer.getInstance().hasPartPos(pos)) {\n                    previousPositionsWithRender.add(pos);\n                }\n            }\n\n            if (rawNetworkData != null) {\n                List<ObservablePartData> parts = Lists.newArrayList();\n                for (RawPartData rawPartData : rawNetworkData.getParts()) {\n                    ObservablePartData partData = new ObservablePartData(\n                            rawNetworkData.getId(), rawNetworkData.getCables(),\n                            rawPartData.getDimension(), rawPartData.getPos(),\n                            rawPartData.getSide(), rawPartData.getName(),\n                            rawPartData.getLast20TicksDurationNs());\n                    parts.add(partData);\n\n                    // Remove this position from the previously rendered list\n                    PartPos pos = partData.toPartPos();\n                    if (pos != null) {\n                        previousPositionsWithRender.remove(pos);\n                    }\n                }\n\n                // Remove all remaining positions from the renderlist,\n                // because those do not exist anymore.\n                for (PartPos partPos : previousPositionsWithRender) {\n                    NetworkDiagnosticsPartOverlayRenderer.getInstance().removePos(partPos);\n                }\n\n                networkDataParts.putAll(id, parts);\n            }\n\n            Collection<ObservableObserverData> previousObservers = networkDataObservers.removeAll(id);\n\n            // The positions that were being rendered previously\n            Set<PartPos> previousPositionsWithRenderObservers = Sets.newHashSet();\n            for (ObservableObserverData partData : previousObservers) {\n                PartPos pos = partData.toPartPos();\n                if (pos != null && NetworkDiagnosticsPartOverlayRenderer.getInstance().hasPartPos(pos)) {\n                    previousPositionsWithRenderObservers.add(pos);\n                }\n            }\n\n            if (rawNetworkData != null) {\n                List<ObservableObserverData> observers = Lists.newArrayList();\n                for (RawObserverData rawPartData : rawNetworkData.getObservers()) {\n                    ObservableObserverData partData = new ObservableObserverData(\n                            rawNetworkData.getId(),\n                            rawPartData.getDimension(), rawPartData.getPos(),\n                            rawPartData.getSide(), rawPartData.getName(),\n                            rawPartData.getLast20TicksDurationNs());\n                    observers.add(partData);\n\n                    // Remove this position from the previously rendered list\n                    PartPos pos = partData.toPartPos();\n                    if (pos != null) {\n                        previousPositionsWithRenderObservers.remove(pos);\n                    }\n                }\n\n                // Remove all remaining positions from the renderlist,\n                // because those do not exist anymore.\n                for (PartPos partPos : previousPositionsWithRenderObservers) {\n                    NetworkDiagnosticsPartOverlayRenderer.getInstance().removePos(partPos);\n                }\n\n                networkDataObservers.putAll(id, observers);\n            }\n        }\n    }\n\n    public static String getAsJsonString() {\n        Gson gson = new GsonBuilder().setPrettyPrinting().create();\n        JsonObject responseObject = new JsonObject();\n\n        responseObject.add(\"localization\", getLocalizationJson());\n        responseObject.add(\"parts\", getPartsJson());\n        responseObject.add(\"observers\", getObserversJson());\n\n        return gson.toJson(responseObject) + \"\\n\";\n    }\n\n    private static JsonElement getLocalizationJson() {\n        JsonObject jsonObject = new JsonObject();\n        for (String entry : LOCALIZE_ENTRIES) {\n            jsonObject.addProperty(entry, IModHelpers.get().getL10NHelpers().localize(entry));\n        }\n        return jsonObject;\n    }\n\n    private static JsonElement getPartsJson() {\n        JsonArray jsonArray = new JsonArray();\n        synchronized (networkDataParts) {\n            for (Map.Entry<Integer, ObservablePartData> entry : networkDataParts.entries()) {\n                JsonObject jsonPart = new JsonObject();\n                ObservablePartData part = entry.getValue();\n                jsonPart.addProperty(\"network\", part.getNetworkId());\n                jsonPart.addProperty(\"cables\", part.getNetworkCables());\n                jsonPart.addProperty(\"part\", IModHelpers.get().getL10NHelpers().localize(part.getName()));\n                jsonPart.addProperty(\"ticktime\", Float.parseFloat(String.format(Locale.ENGLISH, \"%.6f\", ((double) part.getLast20TicksDurationNs()) / IModHelpers.get().getMinecraftHelpers().getSecondInTicks() / 1000000)));\n                jsonPart.addProperty(\"dimension\", part.getDimension().identifier().toString());\n                jsonPart.addProperty(\"position\", part.getPos().toShortString());\n                jsonPart.addProperty(\"side\", part.getSide().name());\n                jsonPart.addProperty(\"highlighted\", NetworkDiagnosticsPartOverlayRenderer.getInstance().hasPartPos(part.toPartPos()));\n                jsonArray.add(jsonPart);\n            }\n        }\n        return jsonArray;\n    }\n\n    private static JsonElement getObserversJson() {\n        JsonArray jsonArray = new JsonArray();\n        synchronized (networkDataParts) {\n            for (Map.Entry<Integer, ObservableObserverData> entry : networkDataObservers.entries()) {\n                JsonObject jsonPart = new JsonObject();\n                ObservableObserverData observer = entry.getValue();\n                jsonPart.addProperty(\"network\", observer.getNetworkId());\n                jsonPart.addProperty(\"part\", observer.getName());\n                jsonPart.addProperty(\"ticktime\", String.format(\"%.6f\", ((double) observer.getLast20TicksDurationNs()) / IModHelpers.get().getMinecraftHelpers().getSecondInTicks() / 1000000));\n                jsonPart.addProperty(\"dimension\", observer.getDimension().identifier().toString());\n                jsonPart.addProperty(\"position\", observer.getPos().toShortString());\n                jsonPart.addProperty(\"side\", observer.getSide() != null ? observer.getSide().name() : \"null\");\n                jsonPart.addProperty(\"highlighted\", NetworkDiagnosticsPartOverlayRenderer.getInstance().hasPartPos(observer.toPartPos()));\n                jsonArray.add(jsonPart);\n            }\n        }\n        return jsonArray;\n    }\n\n    public static void highlightEnable(JsonObject data) {\n        PartPos pos = parsePosString(data.get(\"dimension\").getAsString(), data.get(\"position\").getAsString(), data.get(\"side\").getAsString());\n        NetworkDiagnosticsPartOverlayRenderer.getInstance().addPos(pos);\n    }\n\n    public static void highlightDisable(JsonObject data) {\n        PartPos pos = parsePosString(data.get(\"dimension\").getAsString(), data.get(\"position\").getAsString(), data.get(\"side\").getAsString());\n        if (NetworkDiagnosticsPartOverlayRenderer.getInstance().hasPartPos(pos)) {\n            NetworkDiagnosticsPartOverlayRenderer.getInstance().removePos(pos);\n        }\n    }\n\n    public static void teleport(JsonObject data) {\n        PartPos pos = parsePosString(data.get(\"dimension\").getAsString(), data.get(\"position\").getAsString(), data.get(\"side\").getAsString());\n        BlockPos blockPos = pos.getPos().getBlockPos();\n        float yaw = 0;\n        if (pos.getSide() != null) {\n            blockPos = blockPos.relative(pos.getSide());\n            yaw = pos.getSide().getOpposite().toYRot();\n        }\n        IntegratedDynamics._instance.getPacketHandler().sendToServer(new PlayerTeleportPacket(\n                pos.getPos().getLevelKey(),\n                blockPos.getX(),\n                blockPos.getY() - 1,\n                blockPos.getZ(),\n                yaw,\n                0\n        ));\n    }\n\n    private static PartPos parsePosString(String dimensionString, String positionString, String sideString) {\n        String[] posParts = positionString.split(\", \");\n        BlockPos pos = new BlockPos(Integer.parseInt(posParts[0]), Integer.parseInt(posParts[1]), Integer.parseInt(posParts[2]));\n        return PartPos.of(DimPos.of(dimensionString, pos), Direction.byName(sideString.toLowerCase(Locale.ROOT)));\n    }\n\n    private static class ObservablePartData {\n        private final int networkId;\n        private final int networkCables;\n        private final ResourceKey<Level> dimension;\n        private final BlockPos pos;\n        private final Direction side;\n        private final String name;\n        private final long last20TicksDurationNs;\n\n        public ObservablePartData(int networkId, int networkCables, ResourceKey<Level> dimension, BlockPos pos, Direction side, String name, long last20TicksDurationNs) {\n            this.networkId = networkId;\n            this.networkCables = networkCables;\n            this.dimension = dimension;\n            this.pos = pos;\n            this.side = side;\n            this.name = name;\n            this.last20TicksDurationNs = last20TicksDurationNs;\n        }\n\n        public int getNetworkId() {\n            return networkId;\n        }\n\n        public int getNetworkCables() {\n            return networkCables;\n        }\n\n        public ResourceKey<Level> getDimension() {\n            return dimension;\n        }\n\n        public BlockPos getPos() {\n            return pos;\n        }\n\n        public Direction getSide() {\n            return side;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public long getLast20TicksDurationNs() {\n            return last20TicksDurationNs;\n        }\n\n        public PartPos toPartPos() {\n            Level world = Minecraft.getInstance().level;\n            if (getDimension().identifier().equals(world.dimension().identifier())) {\n                return PartPos.of(DimPos.of(world, getPos()), getSide());\n            }\n            return null;\n        }\n    }\n\n    private static class ObservableObserverData {\n        private final int networkId;\n        private final ResourceKey<Level> dimension;\n        private final BlockPos pos;\n        private final Direction side;\n        private final String name;\n        private final long last20TicksDurationNs;\n\n        public ObservableObserverData(int networkId, ResourceKey<Level> dimension, BlockPos pos, Direction side, String name, long last20TicksDurationNs) {\n            this.networkId = networkId;\n            this.dimension = dimension;\n            this.pos = pos;\n            this.side = side;\n            this.name = name;\n            this.last20TicksDurationNs = last20TicksDurationNs;\n        }\n\n        public int getNetworkId() {\n            return networkId;\n        }\n\n        public ResourceKey<Level> getDimension() {\n            return dimension;\n        }\n\n        public BlockPos getPos() {\n            return pos;\n        }\n\n        public Direction getSide() {\n            return side;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public long getLast20TicksDurationNs() {\n            return last20TicksDurationNs;\n        }\n\n        public PartPos toPartPos() {\n            Level world = Minecraft.getInstance().level;\n            if (getDimension().identifier().equals(world.dimension().identifier())) {\n                return PartPos.of(DimPos.of(world, getPos()), getSide());\n            }\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/NetworkDiagnostics.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.network.*;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;\nimport org.cyclops.integrateddynamics.network.packet.NetworkDiagnosticsNetworkPacket;\n\nimport java.util.*;\n\n/**\n * @author rubensworks\n */\npublic class NetworkDiagnostics {\n\n    private static final NetworkDiagnostics _INSTANCE = new NetworkDiagnostics();\n\n    private final List<UUID> players = Lists.newArrayList();\n    private final Map<UUID, MeasurementSession> activeMeasurements = Maps.newHashMap();\n    private final Set<UUID> playerMeasurements = Sets.newHashSet(); // Track which measurements are player-based\n    private final Map<UUID, UUID> playerToMeasurement = Maps.newHashMap(); // Map player UUID to measurement UUID\n\n    private NetworkDiagnostics() {\n\n    }\n\n    public static NetworkDiagnostics getInstance() {\n        return _INSTANCE;\n    }\n\n    protected ServerPlayer getPlayer(UUID uuid) {\n        return ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayer(uuid);\n    }\n\n    public synchronized void registerPlayer(ServerPlayer player) {\n        if (!players.contains(player.getUUID())) {\n            players.add(player.getUUID());\n            for (INetwork network : NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getNetworks()) {\n                sendNetworkUpdateToPlayer(player, network);\n            }\n\n        }\n    }\n\n    public synchronized void unRegisterPlayer(ServerPlayer player) {\n        players.remove(player.getUUID());\n    }\n\n    public void sendNetworkUpdateToPlayer(ServerPlayer player, INetwork network) {\n        List<RawPartData> rawParts = Lists.newArrayList();\n        for (INetworkElement networkElement : network.getElements()) {\n            if (network.isValid(networkElement) && networkElement instanceof IPartNetworkElement) {\n                IPartNetworkElement partNetworkElement = (IPartNetworkElement) networkElement;\n                PartPos pos = partNetworkElement.getTarget().getCenter();\n                long lastSecondDurationNs = network.getLastSecondDuration(networkElement);\n                rawParts.add(new RawPartData(pos.getPos().getLevelKey(),\n                        pos.getPos().getBlockPos(), pos.getSide(),\n                        partNetworkElement.getPart().getTranslationKey(),\n                        lastSecondDurationNs));\n            } else {\n                // If needed, we can send the other part types later on as well\n            }\n        }\n\n        List<RawObserverData> rawObservers = Lists.newArrayList();\n        for (IFullNetworkListener fullNetworkListener : network.getFullNetworkListeners()) {\n            if (fullNetworkListener instanceof IPositionedAddonsNetworkIngredients) {\n                IPositionedAddonsNetworkIngredients<?, ?> networkIngredients = (IPositionedAddonsNetworkIngredients<?, ?>) fullNetworkListener;\n                Map<PartPos, Long> durations = networkIngredients.getLastSecondDurationIndex();\n                for (Map.Entry<PartPos, Long> durationEntry : durations.entrySet()) {\n                    PartPos pos = durationEntry.getKey();\n                    rawObservers.add(new RawObserverData(pos.getPos().getLevelKey(),\n                            pos.getPos().getBlockPos(), pos.getSide(),\n                            networkIngredients.getComponent().getName().toString(), durationEntry.getValue()));\n                }\n            }\n        }\n\n        RawNetworkData rawNetworkData = new RawNetworkData(network.isKilled(), network.hashCode(), network.getCablesCount(), rawParts, rawObservers);\n        IntegratedDynamics._instance.getPacketHandler().sendToPlayer(new NetworkDiagnosticsNetworkPacket(rawNetworkData.toNbt()), player);\n    }\n\n    public synchronized void sendNetworkUpdate(INetwork network) {\n        for (Iterator<UUID> it = players.iterator(); it.hasNext();) {\n            UUID uuid = it.next();\n            ServerPlayer player = getPlayer(uuid);\n            if (player != null) {\n                sendNetworkUpdateToPlayer(player, network);\n            } else {\n                it.remove();\n            }\n        }\n    }\n\n    public synchronized boolean isBeingDiagnozed() {\n        return !players.isEmpty() || !activeMeasurements.isEmpty();\n    }\n\n    /**\n     * Check if a measurement session is complete for the given player.\n     * @param playerId The UUID of the player\n     * @return true if the measurement is complete, false otherwise\n     */\n    public synchronized boolean isMeasurementComplete(UUID playerId) {\n        MeasurementSession session = activeMeasurements.get(playerId);\n        if (session == null) {\n            return false;\n        }\n        return session.isComplete();\n    }\n\n    /**\n     * Get the total tick time in milliseconds for a completed measurement.\n     * @param playerId The UUID of the player\n     * @return The total tick time in milliseconds, or 0.0 if measurement not complete or not found\n     */\n    public synchronized double getMeasurementTotalTickTime(UUID playerId) {\n        MeasurementSession session = activeMeasurements.get(playerId);\n        if (session == null || !session.isComplete()) {\n            return 0.0;\n        }\n\n        long totalNanoseconds = 0;\n        for (Long time : session.getPartTimes().values()) {\n            totalNanoseconds += time;\n        }\n        for (Long time : session.getObserverTimes().values()) {\n            totalNanoseconds += time;\n        }\n\n        return totalNanoseconds / 1_000_000.0;\n    }\n\n    /**\n     * Get the average tick time in milliseconds for a completed measurement.\n     * @param playerId The UUID of the player\n     * @return The average tick time per game tick in milliseconds, or 0.0 if measurement not complete or not found\n     */\n    public synchronized double getMeasurementAverageTickTime(UUID playerId) {\n        MeasurementSession session = activeMeasurements.get(playerId);\n        if (session == null || !session.isComplete()) {\n            return 0.0;\n        }\n\n        long totalNanoseconds = 0;\n        for (Long time : session.getPartTimes().values()) {\n            totalNanoseconds += time;\n        }\n        for (Long time : session.getObserverTimes().values()) {\n            totalNanoseconds += time;\n        }\n\n        double totalMilliseconds = totalNanoseconds / 1_000_000.0;\n        int totalTicks = session.getDurationSeconds() * 20;\n        return totalMilliseconds / totalTicks;\n    }\n\n    /**\n     * Clear a measurement after it has been read.\n     * This is useful for non-player measurements that persist until explicitly cleared.\n     * @param measurementId The UUID of the measurement to clear\n     */\n    public synchronized void clearMeasurement(UUID measurementId) {\n        activeMeasurements.remove(measurementId);\n        playerMeasurements.remove(measurementId);\n    }\n\n    /**\n     * Start a measurement session without a player.\n     * This is useful for automated benchmarking where no player is online.\n     * Measurements started this way will persist until explicitly cleared via {@link #clearMeasurement(UUID)}.\n     * @param measurementId A unique identifier for the measurement (e.g., a UUID or string)\n     * @param durationSeconds Duration of the measurement in seconds\n     * @return The UUID used internally for tracking this measurement\n     */\n    public synchronized UUID startMeasurementWithoutPlayer(String measurementId, int durationSeconds) {\n        UUID internalId = UUID.nameUUIDFromBytes(measurementId.getBytes());\n        if (activeMeasurements.containsKey(internalId)) {\n            throw new IllegalStateException(String.format(\"Measurement with id %s (%s) already exists\", internalId, measurementId));\n        }\n\n        MeasurementSession session = new MeasurementSession(internalId, durationSeconds);\n        activeMeasurements.put(internalId, session);\n        // Note: NOT adding to playerMeasurements, so it won't be auto-removed\n        return internalId;\n    }\n\n    /**\n     * Start a measurement session for a player.\n     * @param player The player initiating the measurement\n     * @param durationSeconds Duration of the measurement in seconds\n     */\n    public synchronized void startMeasurement(ServerPlayer player, int durationSeconds) {\n        UUID playerId = player.getUUID();\n\n        // Check if this player already has an active measurement\n        if (playerToMeasurement.containsKey(playerId)) {\n            player.sendSystemMessage(Component.literal(\"A measurement is already running for you. Please wait for it to complete.\")\n                    .withStyle(ChatFormatting.RED));\n            return;\n        }\n\n        UUID measurementUUID = startMeasurementWithoutPlayer(\"player:\" + playerId, durationSeconds);\n\n        if (measurementUUID != null) {\n            playerMeasurements.add(measurementUUID); // Mark as player measurement for auto-removal\n            playerToMeasurement.put(playerId, measurementUUID); // Track which measurement belongs to this player\n        }\n\n        player.sendSystemMessage(Component.literal(\"Started measuring network tick times for \" + durationSeconds + \" seconds...\")\n                .withStyle(ChatFormatting.GREEN));\n    }\n\n    /**\n     * Accumulate tick times for active measurement sessions.\n     */\n    public synchronized void accumulateMeasurements() {\n        if (activeMeasurements.isEmpty()) {\n            return;\n        }\n\n        for (INetwork network : NetworkWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getNetworks()) {\n            // Accumulate individual parts\n            for (INetworkElement element : network.getElements()) {\n                long elementDuration = network.getLastSecondDuration(element);\n                if (elementDuration > 0 && element instanceof IPartNetworkElement) {\n                    IPartNetworkElement partElement = (IPartNetworkElement) element;\n                    PartPos pos = partElement.getTarget().getCenter();\n                    String partName = partElement.getPart().getUniqueName().toString();\n                    String dimension = pos.getPos().getLevelKey().identifier().toString();\n                    int x = pos.getPos().getBlockPos().getX();\n                    int y = pos.getPos().getBlockPos().getY();\n                    int z = pos.getPos().getBlockPos().getZ();\n\n                    PartInfo partInfo = new PartInfo(partName, dimension, x, y, z);\n\n                    for (MeasurementSession session : activeMeasurements.values()) {\n                        session.accumulate(partInfo, elementDuration);\n                    }\n                }\n            }\n\n            // Accumulate individual observers\n            for (IFullNetworkListener fullNetworkListener : network.getFullNetworkListeners()) {\n                if (fullNetworkListener instanceof IPositionedAddonsNetworkIngredients) {\n                    IPositionedAddonsNetworkIngredients<?, ?> networkIngredients = (IPositionedAddonsNetworkIngredients<?, ?>) fullNetworkListener;\n                    Map<PartPos, Long> durations = networkIngredients.getLastSecondDurationIndex();\n                    String componentName = networkIngredients.getComponent().getName().toString();\n\n                    for (Map.Entry<PartPos, Long> durationEntry : durations.entrySet()) {\n                        long observerDuration = durationEntry.getValue();\n                        if (observerDuration > 0) {\n                            PartPos pos = durationEntry.getKey();\n                            String dimension = pos.getPos().getLevelKey().identifier().toString();\n                            int x = pos.getPos().getBlockPos().getX();\n                            int y = pos.getPos().getBlockPos().getY();\n                            int z = pos.getPos().getBlockPos().getZ();\n\n                            ObserverInfo observerInfo = new ObserverInfo(componentName, dimension, x, y, z);\n\n                            for (MeasurementSession session : activeMeasurements.values()) {\n                                session.accumulateObserver(observerInfo, observerDuration);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        // Increment the second counter for all sessions\n        for (MeasurementSession session : activeMeasurements.values()) {\n            session.incrementSecond();\n        }\n    }\n\n    /**\n     * Check and complete any finished measurements.\n     * Player-based measurements are automatically removed after completion and results are sent.\n     * Non-player measurements (e.g., from automated tests) persist until explicitly cleared.\n     */\n    public synchronized void checkCompleteMeasurements() {\n        Iterator<Map.Entry<UUID, MeasurementSession>> it = activeMeasurements.entrySet().iterator();\n        while (it.hasNext()) {\n            Map.Entry<UUID, MeasurementSession> entry = it.next();\n            UUID measurementId = entry.getKey();\n            MeasurementSession session = entry.getValue();\n\n            if (session.isComplete()) {\n                // Only auto-remove player-based measurements\n                if (playerMeasurements.contains(measurementId)) {\n                    // Find the player who owns this measurement\n                    UUID playerId = null;\n                    for (Map.Entry<UUID, UUID> playerEntry : playerToMeasurement.entrySet()) {\n                        if (playerEntry.getValue().equals(measurementId)) {\n                            playerId = playerEntry.getKey();\n                            break;\n                        }\n                    }\n\n                    if (playerId != null) {\n                        ServerPlayer player = getPlayer(playerId);\n                        if (player != null) {\n                            sendMeasurementResults(player, session);\n                        }\n                        playerToMeasurement.remove(playerId);\n                    }\n\n                    playerMeasurements.remove(measurementId);\n                    it.remove();\n                }\n                // Non-player measurements persist for explicit reading\n            }\n        }\n    }\n\n    /**\n     * Send measurement results to the player.\n     */\n    private void sendMeasurementResults(ServerPlayer player, MeasurementSession session) {\n        if (session.getPartTimes().isEmpty() && session.getObserverTimes().isEmpty()) {\n            player.sendSystemMessage(Component.literal(\"No network elements found during measurement period.\")\n                    .withStyle(ChatFormatting.YELLOW));\n            return;\n        }\n\n        // Calculate totals\n        long totalNanoseconds = 0;\n        for (Long time : session.getPartTimes().values()) {\n            totalNanoseconds += time;\n        }\n        for (Long time : session.getObserverTimes().values()) {\n            totalNanoseconds += time;\n        }\n\n        double totalMilliseconds = totalNanoseconds / 1_000_000.0;\n        double totalSeconds = totalNanoseconds / 1_000_000_000.0;\n\n        // Format main result\n        player.sendSystemMessage(Component.literal(\"=== Network Tick Time Measurement Results ===\")\n                .withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD));\n        player.sendSystemMessage(Component.literal(\"Measurement Duration: \" + session.getDurationSeconds() + \" seconds\")\n                .withStyle(ChatFormatting.GRAY));\n        player.sendSystemMessage(Component.literal(\"\"));\n\n        player.sendSystemMessage(Component.literal(\"Total Tick Time: \")\n                .withStyle(ChatFormatting.AQUA)\n                .append(Component.literal(String.format(\"%.2f ms (%.3f s)\", totalMilliseconds, totalSeconds))\n                        .withStyle(ChatFormatting.WHITE)));\n\n        // Calculate average per tick (20 ticks per second)\n        int totalTicks = session.getDurationSeconds() * 20;\n        double avgPerTick = totalMilliseconds / totalTicks;\n        player.sendSystemMessage(Component.literal(\"Average per Tick: \")\n                .withStyle(ChatFormatting.AQUA)\n                .append(Component.literal(String.format(\"%.4f ms\", avgPerTick))\n                        .withStyle(ChatFormatting.WHITE)));\n\n        // Show top-5 network parts\n        if (!session.getPartTimes().isEmpty()) {\n            player.sendSystemMessage(Component.literal(\"\"));\n            player.sendSystemMessage(Component.literal(\"Top Network Parts:\")\n                    .withStyle(ChatFormatting.YELLOW));\n\n            // Sort by time descending\n            List<Map.Entry<PartInfo, Long>> sortedParts = Lists.newArrayList(session.getPartTimes().entrySet());\n            sortedParts.sort((a, b) -> Long.compare(b.getValue(), a.getValue()));\n\n            int count = 0;\n            for (Map.Entry<PartInfo, Long> entry : sortedParts) {\n                if (count >= 5) break;\n\n                PartInfo info = entry.getKey();\n                double partMs = entry.getValue() / 1_000_000.0;\n                double percentage = (entry.getValue() * 100.0) / totalNanoseconds;\n                player.sendSystemMessage(Component.literal(String.format(\"  %d. %s @ (%d, %d, %d) [%s]: %.2f ms (%.1f%%)\",\n                        count + 1, info.partName, info.x, info.y, info.z, info.dimension, partMs, percentage))\n                        .withStyle(ChatFormatting.WHITE));\n                count++;\n            }\n        }\n\n        // Show top-3 observers if any exist\n        if (!session.getObserverTimes().isEmpty()) {\n            player.sendSystemMessage(Component.literal(\"\"));\n            player.sendSystemMessage(Component.literal(\"Top Observers:\")\n                    .withStyle(ChatFormatting.YELLOW));\n\n            // Sort by time descending\n            List<Map.Entry<ObserverInfo, Long>> sortedObservers = Lists.newArrayList(session.getObserverTimes().entrySet());\n            sortedObservers.sort((a, b) -> Long.compare(b.getValue(), a.getValue()));\n\n            int count = 0;\n            for (Map.Entry<ObserverInfo, Long> entry : sortedObservers) {\n                if (count >= 3) break;\n\n                ObserverInfo info = entry.getKey();\n                double obsMs = entry.getValue() / 1_000_000.0;\n                double percentage = (entry.getValue() * 100.0) / totalNanoseconds;\n                player.sendSystemMessage(Component.literal(String.format(\"  %d. %s @ (%d, %d, %d) [%s]: %.2f ms (%.1f%%)\",\n                        count + 1, info.componentName, info.x, info.y, info.z, info.dimension, obsMs, percentage))\n                        .withStyle(ChatFormatting.WHITE));\n                count++;\n            }\n        }\n\n        player.sendSystemMessage(Component.literal(\"===========================================\")\n                .withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD));\n    }\n\n    /**\n     * Internal class to track a measurement session.\n     */\n    private static class MeasurementSession {\n        private final UUID playerId;\n        private final int durationSeconds;\n        private final Map<PartInfo, Long> partTimes = Maps.newHashMap();\n        private final Map<ObserverInfo, Long> observerTimes = Maps.newHashMap();\n        private int secondsAccumulated = 0;\n\n        public MeasurementSession(UUID playerId, int durationSeconds) {\n            this.playerId = playerId;\n            this.durationSeconds = durationSeconds;\n        }\n\n        public void accumulate(PartInfo part, long durationNs) {\n            partTimes.merge(part, durationNs, Long::sum);\n        }\n\n        public void accumulateObserver(ObserverInfo observer, long durationNs) {\n            observerTimes.merge(observer, durationNs, Long::sum);\n        }\n\n        public void incrementSecond() {\n            secondsAccumulated++;\n        }\n\n        public boolean isComplete() {\n            return secondsAccumulated >= durationSeconds;\n        }\n\n        public Map<PartInfo, Long> getPartTimes() {\n            return partTimes;\n        }\n\n        public Map<ObserverInfo, Long> getObserverTimes() {\n            return observerTimes;\n        }\n\n        public int getDurationSeconds() {\n            return durationSeconds;\n        }\n    }\n\n    /**\n     * Information about a network part for tracking.\n     */\n    private static class PartInfo {\n        public final String partName;\n        public final String dimension;\n        public final int x, y, z;\n\n        public PartInfo(String partName, String dimension, int x, int y, int z) {\n            this.partName = partName;\n            this.dimension = dimension;\n            this.x = x;\n            this.y = y;\n            this.z = z;\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            PartInfo partInfo = (PartInfo) o;\n            return x == partInfo.x && y == partInfo.y && z == partInfo.z &&\n                    partName.equals(partInfo.partName) && dimension.equals(partInfo.dimension);\n        }\n\n        @Override\n        public int hashCode() {\n            return (31 * (31 * (31 * (31 * partName.hashCode() + dimension.hashCode()) + x) + y) + z);\n        }\n    }\n\n    /**\n     * Information about a network observer for tracking.\n     */\n    private static class ObserverInfo {\n        public final String componentName;\n        public final String dimension;\n        public final int x, y, z;\n\n        public ObserverInfo(String componentName, String dimension, int x, int y, int z) {\n            this.componentName = componentName;\n            this.dimension = dimension;\n            this.x = x;\n            this.y = y;\n            this.z = z;\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            ObserverInfo that = (ObserverInfo) o;\n            return x == that.x && y == that.y && z == that.z &&\n                    componentName.equals(that.componentName) && dimension.equals(that.dimension);\n        }\n\n        @Override\n        public int hashCode() {\n            return (31 * (31 * (31 * (31 * componentName.hashCode() + dimension.hashCode()) + x) + y) + z);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/NetworkDiagnosticsPartOverlayRenderer.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.rendertype.RenderTypes;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.client.event.RenderLevelStageEvent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @author rubensworks\n */\npublic class NetworkDiagnosticsPartOverlayRenderer {\n\n    private static final NetworkDiagnosticsPartOverlayRenderer _INSTANCE = new NetworkDiagnosticsPartOverlayRenderer();\n    private final Set<PartPos> partPositions = Sets.newHashSet();\n\n    private NetworkDiagnosticsPartOverlayRenderer() {\n\n    }\n\n    public static NetworkDiagnosticsPartOverlayRenderer getInstance() {\n        return _INSTANCE;\n    }\n\n    public synchronized void addPos(PartPos pos) {\n        partPositions.add(pos);\n    }\n\n    public synchronized void removePos(PartPos pos) {\n        partPositions.remove(pos);\n    }\n\n    public synchronized void clearPositions() {\n        partPositions.clear();\n    }\n\n    public synchronized boolean hasPartPos(PartPos pos) {\n        return partPositions.contains(pos);\n    }\n\n    @SubscribeEvent\n    public void onRender(RenderLevelStageEvent.AfterSky event) {\n        if (!partPositions.isEmpty()) {\n            Player player = Minecraft.getInstance().player;\n\n            Vec3 eyePos = event.getLevelRenderState().cameraRenderState.pos;\n            double offsetX = eyePos.x;\n            double offsetY = eyePos.y;\n            double offsetZ = eyePos.z;\n\n            List<PartPos> partList = Lists.newArrayList(partPositions);\n            for (PartPos partPos : partList) {\n                if (partPos.getPos().getLevelKey().identifier().equals(player.level().dimension().identifier()) && partPos.getPos().getBlockPos().distSqr(player.blockPosition()) < 10000) {\n                    PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(partPos);\n                    final VoxelShape shape;\n                    if (partStateHolder != null) {\n                        shape = partStateHolder.getPart().getPartRenderPosition().getBoundingBox(partPos.getSide(), CollisionContext.empty());\n                    } else {\n                        shape = Shapes.BLOCK;\n                    }\n\n                    AABB bb = shape\n                            .bounds()\n                            .move(partPos.getPos().getBlockPos())\n                            .move(-offsetX, -offsetY, -offsetZ)\n                            .inflate(0.05, 0.05, 0.05)\n                            .inflate(-0.05, -0.05, -0.05);\n                    IModHelpers.get().getRenderHelpers().renderLineBox(event.getPoseStack(), Minecraft.getInstance().renderBuffers().bufferSource().getBuffer(RenderTypes.lines()),\n                            bb, 1, 0.2f, 0.1f, 0.8f, 6f);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/RawNetworkData.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.ListTag;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * @author rubensworks\n */\npublic class RawNetworkData implements IRawData {\n\n    private final boolean killed;\n    private final int id;\n    private final int cables;\n    private final List<RawPartData> parts;\n    private final List<RawObserverData> observers;\n\n    public RawNetworkData(boolean killed, int id, int cables, List<RawPartData> parts, List<RawObserverData> observers) {\n        this.killed = killed;\n        this.id = id;\n        this.cables = cables;\n        this.parts = parts;\n        this.observers = observers;\n    }\n\n    public boolean isKilled() {\n        return killed;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public int getCables() {\n        return cables;\n    }\n\n    public List<RawPartData> getParts() {\n        return parts;\n    }\n\n    public List<RawObserverData> getObservers() {\n        return observers;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"Network %s (cables: %s; elements: %s)\", id, cables, parts.size());\n    }\n\n    public CompoundTag toNbt() {\n        CompoundTag tag = new CompoundTag();\n        tag.putBoolean(\"killed\", killed);\n        tag.putInt(\"id\", id);\n        tag.putLong(\"cables\", cables);\n\n        ListTag listParts = new ListTag();\n        for (RawPartData part : parts) {\n            listParts.add(part.toNbt());\n        }\n        tag.put(\"parts\", listParts);\n\n        ListTag listObservers = new ListTag();\n        for (RawObserverData observer : observers) {\n            listObservers.add(observer.toNbt());\n        }\n        tag.put(\"observers\", listObservers);\n\n        return tag;\n    }\n\n    public static RawNetworkData fromNbt(CompoundTag tag) {\n        List<RawPartData> parts = Lists.newArrayList();\n        ListTag listParts = tag.getList(\"parts\").orElseThrow();\n        for (int i = 0; i < listParts.size(); i++) {\n            CompoundTag partTag = listParts.getCompound(i).orElseThrow();\n            parts.add(RawPartData.fromNbt(partTag));\n        }\n\n        List<RawObserverData> observers = Lists.newArrayList();\n        ListTag listObservers = tag.getList(\"observers\").orElseThrow();\n        for (int i = 0; i < listObservers.size(); i++) {\n            CompoundTag observerTag = listObservers.getCompound(i).orElseThrow();\n            observers.add(RawObserverData.fromNbt(observerTag));\n        }\n\n        return new RawNetworkData(\n                tag.getBoolean(\"killed\").orElseThrow(),\n                tag.getInt(\"id\").orElseThrow(),\n                tag.getInt(\"cables\").orElseThrow(),\n                parts,\n                observers\n        );\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        RawNetworkData that = (RawNetworkData) o;\n        return killed == that.killed\n                && id == that.id\n                && cables == that.cables\n                && Objects.equals(parts, that.parts)\n                && Objects.equals(observers, that.observers);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(killed, id, cables, parts, observers);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/RawObserverData.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.Level;\n\nimport java.util.Objects;\n\n/**\n * @author rubensworks\n */\npublic class RawObserverData implements IRawData {\n\n    private final ResourceKey<Level> dimension;\n    private final BlockPos pos;\n    private final Direction side;\n    private final String name;\n    private final long last20TicksDurationNs;\n\n    public RawObserverData(ResourceKey<Level> dimension, BlockPos pos, Direction side, String name, long last20TicksDurationNs) {\n        this.dimension = dimension;\n        this.pos = pos;\n        this.side = side;\n        this.name = name;\n        this.last20TicksDurationNs = last20TicksDurationNs;\n    }\n\n    public ResourceKey<Level> getDimension() {\n        return dimension;\n    }\n\n    public BlockPos getPos() {\n        return pos;\n    }\n\n    public Direction getSide() {\n        return side;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public long getLast20TicksDurationNs() {\n        return last20TicksDurationNs;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"%s: %s,%s,%s,%s (%s)\", name, pos.getX(), pos.getY(), pos.getZ(), side, dimension.identifier());\n    }\n\n    public CompoundTag toNbt() {\n        CompoundTag tag = new CompoundTag();\n        tag.putString(\"dimension\", dimension.identifier().toString());\n        tag.putLong(\"pos\", pos.asLong());\n        if (side != null) {\n            tag.putInt(\"side\", side.ordinal());\n        }\n        tag.putString(\"name\", name);\n        tag.putLong(\"last20TicksDurationNs\", last20TicksDurationNs);\n        return tag;\n    }\n\n    public static RawObserverData fromNbt(CompoundTag tag) {\n        return new RawObserverData(\n                ResourceKey.create(Registries.DIMENSION, Identifier.parse(tag.getString(\"dimension\").orElseThrow())),\n                BlockPos.of(tag.getLong(\"pos\").orElseThrow()),\n                tag.contains(\"side\") ? Direction.values()[tag.getInt(\"side\").orElseThrow()] : null,\n                tag.getString(\"name\").orElseThrow(),\n                tag.getLong(\"last20TicksDurationNs\").orElseThrow()\n        );\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        RawObserverData that = (RawObserverData) o;\n        return last20TicksDurationNs == that.last20TicksDurationNs\n                && Objects.equals(dimension, that.dimension)\n                && Objects.equals(pos, that.pos)\n                && Objects.equals(side, that.side)\n                && Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(dimension, pos, side, name, last20TicksDurationNs);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/RawPartData.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.Level;\n\nimport java.util.Objects;\n\n/**\n * @author rubensworks\n */\npublic class RawPartData implements IRawData {\n\n    private final ResourceKey<Level> dimension;\n    private final BlockPos pos;\n    private final Direction side;\n    private final String name;\n    private final long last20TicksDurationNs;\n\n    public RawPartData(ResourceKey<Level> dimension, BlockPos pos, Direction side, String name, long last20TicksDurationNs) {\n        this.dimension = dimension;\n        this.pos = pos;\n        this.side = side;\n        this.name = name;\n        this.last20TicksDurationNs = last20TicksDurationNs;\n    }\n\n    public ResourceKey<Level> getDimension() {\n        return dimension;\n    }\n\n    public BlockPos getPos() {\n        return pos;\n    }\n\n    public Direction getSide() {\n        return side;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public long getLast20TicksDurationNs() {\n        return last20TicksDurationNs;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"%s: %s,%s,%s,%s (%s)\", name, pos.getX(), pos.getY(), pos.getZ(), side, dimension.identifier());\n    }\n\n    public CompoundTag toNbt() {\n        CompoundTag tag = new CompoundTag();\n        tag.putString(\"dimension\", dimension.identifier().toString());\n        tag.putLong(\"pos\", pos.asLong());\n        tag.putInt(\"side\", side.ordinal());\n        tag.putString(\"name\", name);\n        tag.putLong(\"last20TicksDurationNs\", last20TicksDurationNs);\n        return tag;\n    }\n\n    public static RawPartData fromNbt(CompoundTag tag) {\n        return new RawPartData(\n                ResourceKey.create(Registries.DIMENSION, Identifier.parse(tag.getString(\"dimension\").orElseThrow())),\n                BlockPos.of(tag.getLong(\"pos\").orElseThrow()),\n                Direction.values()[tag.getInt(\"side\").orElseThrow()],\n                tag.getString(\"name\").orElseThrow(),\n                tag.getLong(\"last20TicksDurationNs\").orElseThrow()\n        );\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        RawPartData that = (RawPartData) o;\n        return last20TicksDurationNs == that.last20TicksDurationNs\n                && Objects.equals(dimension, that.dimension)\n                && Objects.equals(pos, that.pos)\n                && Objects.equals(side, that.side)\n                && Objects.equals(name, that.name);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(dimension, pos, side, name, last20TicksDurationNs);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/RawRootData.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics;\n\n/**\n * @author rubensworks\n */\npublic class RawRootData implements IRawData {\n\n    @Override\n    public String toString() {\n        return \"Networks\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/http/DiagnosticsWebServer.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics.http;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport org.apache.logging.log4j.Level;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * An HTTP server that holds a single channel.\n * @author rubensworks\n */\npublic class DiagnosticsWebServer {\n\n    private final int port;\n\n    private EventLoopGroup bossGroup;\n    private EventLoopGroup workerGroup;\n    private Channel channel;\n\n    public DiagnosticsWebServer(int port) {\n        this.port = port;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public String getUrl() {\n        return String.format(\"http://localhost:%s/\", getPort());\n    }\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void initialize() {\n        IntegratedDynamics.clog(Level.INFO, \"Starting local Integrated Dynamics network diagnostics server...\");\n        this.bossGroup = new NioEventLoopGroup(1);\n        this.workerGroup = new NioEventLoopGroup();\n\n        ServerBootstrap b = new ServerBootstrap();\n        b.group(bossGroup, workerGroup)\n                .channel(NioServerSocketChannel.class)\n                .childHandler(new DiagnosticsWebServerInitializer());\n\n        try {\n            this.channel = b.bind(this.port).sync().channel();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n\n        IntegratedDynamics.clog(Level.INFO, \"Started local Integrated Dynamics network diagnostics server on http://localhost:\" + this.port + \"/\");\n    }\n\n    public void deinitialize() {\n        IntegratedDynamics.clog(Level.INFO, \"Stopping local Integrated Dynamics network diagnostics server...\");\n        if (this.bossGroup != null && workerGroup != null) {\n            try {\n                bossGroup.shutdownGracefully().sync();\n                workerGroup.shutdownGracefully().sync();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n        IntegratedDynamics.clog(Level.INFO, \"Stopped local Integrated Dynamics network diagnostics server\");\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/http/DiagnosticsWebServerHandler.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics.http;\n\nimport com.google.gson.JsonParser;\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.ChannelFutureListener;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.SimpleChannelInboundHandler;\nimport io.netty.handler.codec.http.*;\nimport io.netty.util.CharsetUtil;\nimport org.apache.commons.io.IOUtils;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDataClient;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * A handler for HTTP requests.\n * @author rubensworks\n */\npublic class DiagnosticsWebServerHandler extends SimpleChannelInboundHandler<Object> {\n\n    @Override\n    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {\n        ctx.flush();\n    }\n\n    @Override\n    protected void channelRead0(ChannelHandlerContext context, Object message) throws Exception {\n        if (message instanceof HttpRequest) {\n            HttpRequest request = (HttpRequest) message;\n\n            if (HttpUtil.is100ContinueExpected(request)) {\n                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);\n                context.write(response);\n            }\n\n            HttpResponseStatus responseStatus;\n            String responseString;\n            String contentType;\n            switch (request.uri()) {\n                case \"/\":\n                    responseStatus = HttpResponseStatus.OK;\n                    InputStream is = IntegratedDynamics.class.getResourceAsStream(\"/data/\" + Reference.MOD_ID + \"/web/diagnostics.html\");\n                    responseString = IOUtils.toString(is, StandardCharsets.UTF_8);\n                    contentType = \"text/html; charset=UTF-8\";\n                    break;\n                case \"/data.json\":\n                    responseStatus = HttpResponseStatus.OK;\n                    responseString = NetworkDataClient.getAsJsonString();\n                    contentType = \"application/json; charset=UTF-8\";\n                    break;\n                case \"/highlightEnable\":\n                    if (request.method().equals(HttpMethod.POST) && request instanceof HttpContent httpRequest) {\n                        NetworkDataClient.highlightEnable(JsonParser\n                                .parseString(httpRequest.content().toString(CharsetUtil.UTF_8))\n                                .getAsJsonObject());\n                    }\n                    responseStatus = HttpResponseStatus.OK;\n                    responseString = \"Ok\";\n                    contentType = \"text/plain; charset=UTF-8\";\n                    break;\n                case \"/highlightDisable\":\n                    if (request.method().equals(HttpMethod.POST) && request instanceof HttpContent httpRequest) {\n                        NetworkDataClient.highlightDisable(JsonParser\n                                .parseString(httpRequest.content().toString(CharsetUtil.UTF_8))\n                                .getAsJsonObject());\n                    }\n                    responseStatus = HttpResponseStatus.OK;\n                    responseString = \"Ok\";\n                    contentType = \"text/plain; charset=UTF-8\";\n                    break;\n                case \"/teleport\":\n                    if (request.method().equals(HttpMethod.POST) && request instanceof HttpContent httpRequest) {\n                        NetworkDataClient.teleport(JsonParser\n                                .parseString(httpRequest.content().toString(CharsetUtil.UTF_8))\n                                .getAsJsonObject());\n                    }\n                    responseStatus = HttpResponseStatus.OK;\n                    responseString = \"Ok\";\n                    contentType = \"text/plain; charset=UTF-8\";\n                    break;\n                default:\n                    responseStatus = HttpResponseStatus.NOT_FOUND;\n                    responseString = \"Not found\";\n                    contentType = \"text/plain; charset=UTF-8\";\n                    break;\n            }\n\n            if (!writeResponse(request, context, responseString, contentType, responseStatus)) {\n                // If keep-alive is off, close the connection once the content is fully written.\n                context.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);\n            }\n        }\n    }\n\n    private boolean writeResponse(HttpRequest request, ChannelHandlerContext context,\n                                  String responseString, String contentType,\n                                  HttpResponseStatus responseStatus) {\n        // Decide whether to close the connection or not.\n        boolean keepAlive = HttpUtil.isKeepAlive(request);\n        // Build the response object.\n        FullHttpResponse response = new DefaultFullHttpResponse(\n                HttpVersion.HTTP_1_1, responseStatus,\n                Unpooled.copiedBuffer(responseString, CharsetUtil.UTF_8));\n\n        response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);\n\n        if (keepAlive) {\n            // Add 'Content-Length' header only for a keep-alive connection.\n            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());\n            // Add keep alive header as per:\n            // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection\n            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);\n        }\n\n        // Write the response.\n        context.write(response);\n\n        return keepAlive;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/diagnostics/http/DiagnosticsWebServerInitializer.java",
    "content": "package org.cyclops.integrateddynamics.core.network.diagnostics.http;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.handler.codec.http.HttpObjectAggregator;\nimport io.netty.handler.codec.http.HttpRequestDecoder;\nimport io.netty.handler.codec.http.HttpResponseEncoder;\n\n/**\n * Initializes an HTTP server.\n * @author rubensworks\n */\npublic class DiagnosticsWebServerInitializer extends ChannelInitializer<SocketChannel> {\n    @Override\n    protected void initChannel(SocketChannel ch) throws Exception {\n        ChannelPipeline p = ch.pipeline();\n        p.addLast(new HttpRequestDecoder());\n        p.addLast(new HttpResponseEncoder());\n        p.addLast(new HttpObjectAggregator(1048576));\n        p.addLast(new DiagnosticsWebServerHandler());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/NetworkElementAddEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.event.ICancelableNetworkEvent;\n\n/**\n * An event thrown when an {@link INetworkElement} is being added to the network.\n * @author rubensworks\n */\npublic abstract class NetworkElementAddEvent extends NetworkEvent {\n\n    private final INetworkElement networkElement;\n\n    protected NetworkElementAddEvent(INetwork network, INetworkElement networkElement) {\n        super(network);\n        this.networkElement = networkElement;\n    }\n\n    public INetworkElement getNetworkElement() {\n        return this.networkElement;\n    }\n\n    /**\n     * A cancelable event before the element is added to the network.\n     * Canceling this event will prevent the network element from being added.\n     */\n    public static class Pre extends NetworkElementAddEvent implements ICancelableNetworkEvent {\n\n        private boolean canceled = false;\n\n        public Pre(INetwork network, INetworkElement networkElement) {\n            super(network, networkElement);\n        }\n\n        @Override\n        public void cancel() {\n            this.canceled = true;\n        }\n\n        @Override\n        public boolean isCanceled() {\n            return this.canceled;\n        }\n\n    }\n\n    /**\n     * After the element has been added to the network.\n     */\n    public static class Post extends NetworkElementAddEvent {\n\n        public Post(INetwork network, INetworkElement networkElement) {\n            super(network, networkElement);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/NetworkElementRemoveEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.event.ICancelableNetworkEvent;\n\n/**\n * An event thrown when an {@link INetworkElement} is being removed from the network.\n * @author rubensworks\n */\npublic abstract class NetworkElementRemoveEvent extends NetworkEvent {\n\n    private final INetworkElement networkElement;\n\n    protected NetworkElementRemoveEvent(INetwork network, INetworkElement networkElement) {\n        super(network);\n        this.networkElement = networkElement;\n    }\n\n    public INetworkElement getNetworkElement() {\n        return this.networkElement;\n    }\n\n    /**\n     * A cancelable event before the element is removed from the network.\n     * Canceling this event will prevent the network element from being removed.\n     */\n    public static class Pre extends NetworkElementRemoveEvent implements ICancelableNetworkEvent {\n\n        private boolean canceled = false;\n\n        public Pre(INetwork network, INetworkElement networkElement) {\n            super(network, networkElement);\n        }\n\n        @Override\n        public void cancel() {\n            this.canceled = true;\n        }\n\n        @Override\n        public boolean isCanceled() {\n            return this.canceled;\n        }\n\n    }\n\n    /**\n     * After the element has been removed from the network.\n     */\n    public static class Post extends NetworkElementRemoveEvent {\n\n        public Post(INetwork network, INetworkElement networkElement) {\n            super(network, networkElement);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/NetworkEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\n\n/**\n * An event posted in the {@link INetwork} event bus.\n * @author rubensworks\n */\npublic class NetworkEvent implements INetworkEvent {\n\n    private final INetwork network;\n\n    public NetworkEvent(INetwork network) {\n        this.network = network;\n    }\n\n    @Override\n    public INetwork getNetwork() {\n        return this.network;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/NetworkEventBus.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport com.google.common.collect.Maps;\nimport net.neoforged.bus.EventBus;\nimport org.cyclops.cyclopscore.helper.CollectionHelpers;\nimport org.cyclops.integrateddynamics.api.network.IEventListenableNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetworkEventListener;\nimport org.cyclops.integrateddynamics.api.network.event.ICancelableNetworkEvent;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEventBus;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * An event bus for {@link INetwork} events where\n * {@link INetworkElement} instances can listen to.\n *\n * Partially based on Minecraft Forge's {@link EventBus} implementation.\n *\n * @author rubensworks\n */\npublic class NetworkEventBus implements INetworkEventBus {\n\n    private final Map<Class<? extends INetworkEvent>, Set<IEventListenableNetworkElement<?>>> listeners = Collections.synchronizedMap(Maps.<Class<? extends INetworkEvent>, Set<IEventListenableNetworkElement<?>>>newHashMap());\n\n    @Override\n    public void register(IEventListenableNetworkElement<?> target, Class<? extends INetworkEvent> eventType) {\n        CollectionHelpers.addToMapSet(this.listeners, eventType, target);\n    }\n\n    @Override\n    public void unregister(IEventListenableNetworkElement<?> target, Class<? extends INetworkEvent> eventType) {\n        Set<IEventListenableNetworkElement<?>> listeners = this.listeners.get(eventType);\n        if(listeners != null) {\n            listeners.remove(target);\n        }\n    }\n\n    @Override\n    public void unregister(IEventListenableNetworkElement<?> target) {\n        target.getNetworkEventListener().ifPresent(listener -> {\n            for(Class<? extends INetworkEvent> eventType : listener.getSubscribedEvents()) {\n                unregister(target, eventType);\n            }\n        });\n    }\n\n    @Override\n    public void post(INetworkEvent event) {\n        Set<IEventListenableNetworkElement<?>> listeners = this.listeners.get(event.getClass());\n        if(listeners != null) {\n            for (IEventListenableNetworkElement<?> listener : listeners) {\n                listener.getNetworkEventListener().ifPresent(l -> ((INetworkEventListener) l).onEvent(event, listener));\n            }\n        }\n    }\n\n    @Override\n    public boolean postCancelable(ICancelableNetworkEvent event) {\n        post(event);\n        return !event.isCanceled();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/NetworkInitializedEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.bus.api.Event;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\n\nimport javax.annotation.Nullable;\n\n/**\n * An event that is posted in the Forge event bus.\n * @author rubensworks\n */\npublic class NetworkInitializedEvent extends Event {\n\n    private final INetwork network;\n    private final Level world;\n    private final BlockPos pos;\n    private final LivingEntity placer;\n\n    public NetworkInitializedEvent(INetwork network, Level world, BlockPos pos, @Nullable LivingEntity placer) {\n        this.network = network;\n        this.world = world;\n        this.pos = pos;\n        this.placer = placer;\n    }\n\n    public INetwork getNetwork() {\n        return network;\n    }\n\n    public Level getLevel() {\n        return world;\n    }\n\n    public BlockPos getPos() {\n        return pos;\n    }\n\n    @Nullable\n    public LivingEntity getPlacer() {\n        return placer;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/UnknownPartEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\nimport javax.annotation.Nullable;\n\n/**\n * An event thrown when a network is loaded with invalid parts.\n * @author rubensworks\n */\npublic class UnknownPartEvent extends NetworkEvent {\n\n    private final String partTypeName;\n    private IPartType partType;\n\n    public UnknownPartEvent(INetwork network, String partTypeName) {\n        super(network);\n        this.partTypeName = partTypeName;\n        this.partType = null;\n    }\n\n    /**\n     * @return The part type.\n     */\n    public @Nullable IPartType getPartType() {\n        return partType;\n    }\n\n    /**\n     * Set the part type to load instead.\n     * @param partType The part type to load.\n     */\n    public void setPartType(IPartType partType) {\n        this.partType = partType;\n    }\n\n    /**\n     * @return The part name that is being loaded but no corresponding part is found in the registry.\n     */\n    public String getPartTypeName() {\n        return partTypeName;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/network/event/VariableContentsUpdatedEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.network.event;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\n\n/**\n * An event used to signal network elements of updated variables inside the network.\n * @author rubensworks\n */\npublic class VariableContentsUpdatedEvent extends NetworkEvent {\n\n    public VariableContentsUpdatedEvent(INetwork network) {\n        super(network);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartStateActiveVariableBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.persist.nbt.NBTClassType;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.block.IVariableContainer;\nimport org.cyclops.integrateddynamics.api.evaluate.IValueInterface;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartCapability;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.capability.variablecontainer.VariableContainerDefault;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * An abstract part state with a focus on activatable variables.\n * @author rubensworks\n */\npublic abstract class PartStateActiveVariableBase<P extends IPartType> extends PartStateBase<P> {\n\n    private boolean checkedForWriteVariable = false;\n    protected IVariableFacade currentVariableFacade = null;\n    private final IVariableContainer variableContainer;\n    private boolean deactivated = false;\n    private SimpleInventory inventory;\n    private List<Component> globalErrorMessages = Lists.newLinkedList();\n    private boolean retryEvaluation = false;\n\n    public boolean isDeactivated() {\n        return deactivated;\n    }\n\n    public void setDeactivated(boolean deactivated) {\n        this.deactivated = deactivated;\n    }\n\n    public boolean isRetryEvaluation() {\n        return retryEvaluation;\n    }\n\n    public void setRetryEvaluation(boolean retryEvaluation) {\n        this.retryEvaluation = retryEvaluation;\n    }\n\n    public PartStateActiveVariableBase(int inventorySize) {\n        this.inventory = new SingularInventory(inventorySize);\n        this.inventory.addDirtyMarkListener(this); // No need to remove myself eventually. If I am removed, inv is also removed.\n        variableContainer = new VariableContainerDefault();\n        addVolatileCapability(Capabilities.VariableContainer.PART, Optional.of(variableContainer));\n    }\n\n    /**\n     * @return The inner inventory\n     */\n    public SimpleInventory getInventory() {\n        return this.inventory;\n    }\n\n    protected void validate(INetwork network, IPartNetwork partNetwork) {\n        // Note that this is only called server-side, so these errors are sent via NBT to the client(s).\n        this.currentVariableFacade.validate(network, partNetwork,\n                new PartStateActiveVariableBase.Validator(this), currentVariableFacade.getOutputType());\n    }\n\n    protected void onCorruptedState() {\n        IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, \"A corrupted part state was found at, repairing...\");\n        Thread.dumpStack();\n        this.checkedForWriteVariable = false;\n        this.deactivated = true;\n    }\n\n    /**\n     * @return If there is an active variable present for this state.\n     */\n    public boolean hasVariable() {\n        return (getGlobalErrors().isEmpty() || isRetryEvaluation()) && !getInventory().isEmpty();\n    }\n\n    /**\n     * Get the active variable in this state.\n     * @param <V> The variable value type.\n     * @param network The network.\n     * @param partNetwork The part network.\n     * @param valueDeseralizationContext\n     * @return The variable.\n     */\n    public <V extends IValue> IVariable<V> getVariable(INetwork network, IPartNetwork partNetwork, ValueDeseralizationContext valueDeseralizationContext) {\n        if(!checkedForWriteVariable) {\n            if (variableContainer.getVariableCache().isEmpty()) {\n                variableContainer.refreshVariables(network, inventory, false, valueDeseralizationContext);\n            }\n            for (IVariableFacade facade : variableContainer.getVariableCache().values()) {\n                if (facade != null) {\n                    currentVariableFacade = facade;\n                    validate(network, partNetwork);\n                }\n            }\n            this.checkedForWriteVariable = true;\n        }\n        if(currentVariableFacade == null) {\n            onCorruptedState();\n            return null;\n        }\n        return currentVariableFacade.getVariable(network, partNetwork);\n    }\n\n    /**\n     * Refresh the current variable to have its current info reset and updated.\n     * @param partType The corresponding part type.\n     * @param target The target of the part.\n     */\n    public void onVariableContentsUpdated(P partType, PartTarget target) {\n        // Resets the errors for this aspect\n        this.checkedForWriteVariable = false;\n        addGlobalError(null);\n        this.currentVariableFacade = null;\n        //this.deactivated = false; // This *should* not be required anymore, re-activation is handled in AspectWriteBase#update.\n\n        // Refresh any contained variables\n        PartPos center = target.getCenter();\n        Level level = center.getPos().getLevel(true);\n        NetworkHelpers.getNetwork(level, center.getPos().getBlockPos(), center.getSide())\n                .ifPresent(network -> variableContainer.refreshVariables(network, inventory, false, ValueDeseralizationContext.of(level)));\n    }\n\n    /**\n     * @return All global error messages.\n     */\n    public List<Component> getGlobalErrors() {\n        return globalErrorMessages;\n    }\n\n    /**\n     * Add a global error message.\n     * @param error The message to add.\n     */\n    public void addGlobalError(Component error) {\n        setRetryEvaluation(false);\n        if(error == null) {\n            globalErrorMessages.clear();\n        } else {\n            globalErrorMessages.add(error);\n        }\n        onDirty();\n        sendUpdate(); // We want this error messages to be sent to the client(s).\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput) {\n        super.serialize(valueOutput);\n        NBTClassType.writeNbt(List.class, \"globalErrorMessages\", globalErrorMessages, valueOutput);\n        inventory.writeToNBT(valueOutput, \"inventory\");\n    }\n\n    @Override\n    public void deserialize(ValueInput valueInput) {\n        super.deserialize(valueInput);\n        //noinspection unchecked\n        this.globalErrorMessages = NBTClassType.readNbt(List.class, \"globalErrorMessages\", valueInput);\n        inventory.readFromNBT(valueInput, \"inventory\");\n    }\n\n    @Override\n    public <T> Optional<T> getCapability(P partType, PartCapability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n        if (capability == Capabilities.ValueInterface.PART) {\n            if (hasVariable()) {\n                IVariable<IValue> variable = getVariable(network, partNetwork, ValueDeseralizationContext.of(target.getCenter().getPos().getLevel(true)));\n                if (variable != null) {\n                    return (Optional<T>) Optional.<IValueInterface>of(() -> Optional.of(variable.getValue()));\n                }\n            }\n            return Optional.empty();\n        }\n        return super.getCapability(partType, capability, network, partNetwork, target);\n    }\n\n    /**\n     * An inventory that can only hold one filled slot at a time.\n     */\n    public static class SingularInventory extends SimpleInventory {\n\n        /**\n         * Make a new instance.\n         *\n         * @param size The amount of slots in the inventory.\n         */\n        public SingularInventory(int size) {\n            super(size, 1);\n        }\n\n        protected boolean canInsert(int slot) {\n            for (int i = 0; i < getContainerSize(); i++) {\n                // Only allow insertion if the target slot is the same as the non-empty slot\n                if (i != slot && !getItem(i).isEmpty()) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        @Override\n        public boolean canPlaceItem(int i, ItemStack itemstack) {\n            return canInsert(i) && super.canPlaceItem(i, itemstack);\n        }\n\n    }\n\n    public static class Validator implements IVariableFacade.IValidator {\n\n        private final PartStateActiveVariableBase state;\n\n        /**\n         * Make a new instance\n         * @param state The part state.\n         */\n        public Validator(PartStateActiveVariableBase state) {\n            this.state = state;\n        }\n\n        @Override\n        public void addError(MutableComponent error) {\n            this.state.addGlobalError(error);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartStateBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport com.google.common.collect.Maps;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.ContainerHelper;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.cyclopscore.persist.nbt.NBTClassType;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.core.evaluate.InventoryVariableEvaluator;\nimport org.cyclops.integrateddynamics.core.part.aspect.property.AspectProperties;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport javax.annotation.Nullable;\nimport java.util.IdentityHashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * A default implementation of the {@link IPartState}.\n * @author rubensworks\n */\npublic abstract class PartStateBase<P extends IPartType> implements IPartState<P>, IDirtyMarkListener {\n\n    private boolean dirty = false;\n    private boolean update = false;\n    private boolean forceBlockUpdateRender = false;\n\n    private int updateInterval = getDefaultUpdateInterval();\n    private int priority = 0;\n    private int channel = 0;\n    private int maxOffset;\n    private Vec3i targetOffset = new Vec3i(0, 0, 0);\n    private Direction targetSide = null;\n    private int id = -1;\n    private Map<IAspect, IAspectProperties> aspectProperties = new IdentityHashMap<>();\n    private boolean enabled = true;\n    private final Map<String, NonNullList<ItemStack>> inventoriesNamed = Maps.newHashMap();\n    private final PartStateOffsetHandler<P> offsetHandler = new PartStateOffsetHandler<>();\n\n    private IdentityHashMap<PartCapability<?>, Optional<Object>> volatileCapabilities = new IdentityHashMap<>();\n\n    @Override\n    public void serialize(ValueOutput valueOutput) {\n        valueOutput.putInt(\"updateInterval\", this.updateInterval);\n        valueOutput.putInt(\"priority\", this.priority);\n        valueOutput.putInt(\"channel\", this.channel);\n        if (this.targetSide != null) {\n            valueOutput.putInt(\"targetSide\", this.targetSide.ordinal());\n        }\n        valueOutput.putInt(\"id\", this.id);\n        writeAspectProperties(valueOutput.child(\"aspectProperties\"));\n        valueOutput.putBoolean(\"enabled\", this.enabled);\n        valueOutput.putInt(\"maxOffset\", this.maxOffset);\n        valueOutput.putInt(\"offsetX\", this.targetOffset.getX());\n        valueOutput.putInt(\"offsetY\", this.targetOffset.getY());\n        valueOutput.putInt(\"offsetZ\", this.targetOffset.getZ());\n\n        // Write inventoriesNamed\n        ValueOutput.ValueOutputList namedInventoriesList = valueOutput.childrenList(\"inventoriesNamed\");\n        for (Map.Entry<String, NonNullList<ItemStack>> entry : this.inventoriesNamed.entrySet()) {\n            ValueOutput listEntry = namedInventoriesList.addChild();\n            listEntry.putString(\"tabName\", entry.getKey());\n            listEntry.putInt(\"itemCount\", entry.getValue().size());\n            ContainerHelper.saveAllItems(listEntry, entry.getValue());\n        }\n\n        ValueOutput.ValueOutputList errorsTag = valueOutput.childrenList(\"offsetVariablesSlotMessages\");\n        for (Int2ObjectMap.Entry<MutableComponent> entry : this.offsetHandler.offsetVariablesSlotMessages.int2ObjectEntrySet()) {\n            ValueOutput child = errorsTag.addChild();\n            String slot = String.valueOf(entry.getIntKey());\n            child.putString(\"slot\", slot);\n            NBTClassType.writeNbt(MutableComponent.class, slot, entry.getValue(), child);\n        }\n    }\n\n    @Override\n    public void deserialize(ValueInput valueInput) {\n        this.updateInterval = valueInput.getInt(\"updateInterval\").orElseThrow();\n        this.priority = valueInput.getInt(\"priority\").orElseThrow();\n        this.channel = valueInput.getInt(\"channel\").orElseThrow();\n        this.targetSide = valueInput.getInt(\"targetSide\")\n                .map(s -> Direction.values()[s])\n                .orElse(null);\n        this.id = valueInput.getInt(\"id\").orElseThrow();\n        this.aspectProperties.clear();\n        readAspectProperties(valueInput.child(\"aspectProperties\").orElseThrow());\n        this.enabled = valueInput.getBooleanOr(\"enabled\", false);\n        this.maxOffset = valueInput.getInt(\"maxOffset\").orElseThrow();\n        this.targetOffset = new Vec3i(\n                valueInput.getInt(\"offsetX\").orElseThrow(),\n                valueInput.getInt(\"offsetY\").orElseThrow(),\n                valueInput.getInt(\"offsetZ\").orElseThrow()\n        );\n\n        // Read inventoriesNamed\n        for (ValueInput listEntry : valueInput.childrenList(\"inventoriesNamed\").orElseThrow()) {\n            NonNullList<ItemStack> list = NonNullList.withSize(listEntry.getInt(\"itemCount\").orElseThrow(), ItemStack.EMPTY);\n            String tabName = listEntry.getString(\"tabName\").orElseThrow();\n            ContainerHelper.loadAllItems(listEntry, list);\n            this.inventoriesNamed.put(tabName, list);\n        }\n\n        this.offsetHandler.offsetVariablesSlotMessages.clear();\n        ValueInput.ValueInputList errorsTag = valueInput.childrenList(\"offsetVariablesSlotMessages\").orElseThrow();\n        for (ValueInput child : errorsTag) {\n            String slot = child.getString(\"slot\").orElseThrow();\n            MutableComponent unlocalizedString = NBTClassType.readNbt(MutableComponent.class, slot, child);\n            this.offsetHandler.offsetVariablesSlotMessages.put(Integer.parseInt(slot), unlocalizedString);\n        }\n    }\n\n    protected void writeAspectProperties(ValueOutput valueOutput) {\n        ValueOutput.ValueOutputList list = valueOutput.childrenList(\"map\");\n        for(Map.Entry<IAspect, IAspectProperties> entry : aspectProperties.entrySet()) {\n            ValueOutput entryTag = list.addChild();\n            entryTag.putString(\"key\", entry.getKey().getUniqueName().toString());\n            if(entry.getValue() != null) {\n                entry.getValue().serialize(entryTag.child(\"value\"));\n            }\n        }\n    }\n\n    public void readAspectProperties(ValueInput valueInput) {\n        for (ValueInput entryTag : valueInput.childrenList(\"map\").orElseThrow()) {\n            IAspect key = Aspects.REGISTRY.getAspect(Identifier.parse(entryTag.getString(\"key\").orElseThrow()));\n            IAspectProperties value = entryTag.child(\"value\")\n                    .map(v -> {\n                        AspectProperties ap = new AspectProperties();\n                        ap.deserialize(v);\n                        return ap;\n                    })\n                    .orElse(null);\n            if (key != null && value != null) {\n                this.aspectProperties.put(key, value);\n            }\n        }\n    }\n\n    @Override\n    public void generateId() {\n        this.id = IntegratedDynamics.globalCounters.get().getNext(IPartState.GLOBALCOUNTER_KEY);\n    }\n\n    @Override\n    public int getId() {\n        return this.id;\n    }\n\n    @Override\n    public void setUpdateInterval(int updateInterval) {\n        this.updateInterval = updateInterval;\n    }\n\n    @Override\n    public int getUpdateInterval() {\n        return updateInterval;\n    }\n\n    @Override\n    public void setPriority(int priority) {\n        this.priority = priority;\n    }\n\n    @Override\n    public int getPriority() {\n        return priority;\n    }\n\n    @Override\n    public void setChannel(int channel) {\n        this.channel = channel;\n    }\n\n    @Override\n    public int getChannel() {\n        return channel;\n    }\n\n    @Override\n    public Vec3i getTargetOffset() {\n        return targetOffset;\n    }\n\n    @Override\n    public void setTargetOffset(Vec3i targetOffset) {\n        this.targetOffset = targetOffset;\n        this.markDirty();\n    }\n\n    @Override\n    public void setTargetSideOverride(Direction targetSide) {\n        this.targetSide = targetSide;\n    }\n\n    @Nullable\n    @Override\n    public Direction getTargetSideOverride() {\n        return targetSide;\n    }\n\n    @Override\n    public void markDirty() {\n        this.dirty = true;\n    }\n\n    @Override\n    public boolean isDirtyAndReset() {\n        boolean wasDirty = this.dirty;\n        this.dirty = false;\n        return wasDirty;\n    }\n\n    @Override\n    public boolean isUpdateAndReset() {\n        boolean wasUpdate = this.update;\n        this.update = false;\n        return wasUpdate;\n    }\n\n    @Override\n    public void forceBlockRenderUpdate() {\n        this.forceBlockUpdateRender = true;\n    }\n\n    @Override\n    public boolean isForceBlockRenderUpdateAndReset() {\n        boolean wasForceBlockUpdateRender = this.forceBlockUpdateRender;\n        this.forceBlockUpdateRender = false;\n        return wasForceBlockUpdateRender;\n    }\n\n    @Override\n    public void onDirty() {\n        this.dirty = true;\n        this.forceBlockRenderUpdate();\n    }\n\n    /**\n     * Enables a flag that tells the part container to send an NBT update to the client(s).\n     */\n    public void sendUpdate() {\n        this.update = true;\n    }\n\n    @Override\n    public IAspectProperties getAspectProperties(IAspect aspect) {\n        return aspectProperties.get(aspect);\n    }\n\n    @Override\n    public void setAspectProperties(IAspect aspect, IAspectProperties properties) {\n        aspectProperties.put(aspect, properties);\n        sendUpdate();\n    }\n\n    @Override\n    public void setEnabled(boolean enabled) {\n        boolean wasEnabled = this.enabled;\n        this.enabled = enabled;\n        if (this.enabled != wasEnabled) {\n            sendUpdate();\n        }\n    }\n\n    @Override\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public NonNullList<ItemStack> getInventoryNamed(String name) {\n        return this.inventoriesNamed.get(name);\n    }\n\n    public void setInventoryNamed(String name, NonNullList<ItemStack> inventory) {\n        this.inventoriesNamed.put(name, inventory);\n        onDirty();\n    }\n\n    @Override\n    public Map<String, NonNullList<ItemStack>> getInventoriesNamed() {\n        return this.inventoriesNamed;\n    }\n\n    @Override\n    public void clearInventoriesNamed() {\n        this.inventoriesNamed.clear();\n    }\n\n    /**\n     * Gathers the capabilities of this part state.\n     * Don't call this unless you know what you're doing!\n     */\n    public void gatherCapabilities(P partType) {\n        AttachCapabilitiesEventPart event = new AttachCapabilitiesEventPart(partType, this);\n        NeoForge.EVENT_BUS.post(event);\n    }\n\n    @Override\n    public <T> Optional<T> getCapability(P partType, PartCapability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n        Optional<Object> o = volatileCapabilities.get(capability);\n        if(o != null && o.isPresent()) {\n            return (Optional<T>) o;\n        }\n        return Optional.ofNullable(capability.getCapability(partType, target));\n    }\n\n    @Override\n    public <T> void addVolatileCapability(PartCapability<T> capability, Optional<T> value) {\n        volatileCapabilities.put(capability, (Optional<Object>) value);\n    }\n\n    @Override\n    public void removeVolatileCapability(PartCapability<?> capability) {\n        volatileCapabilities.remove(capability);\n    }\n\n    protected int getDefaultUpdateInterval() {\n        return GeneralConfig.defaultPartUpdateFreq;\n    }\n\n    @Override\n    public void initializeOffsets(PartTarget target) {\n        this.offsetHandler.initializeVariableEvaluators(this.offsetHandler.getOffsetVariablesInventory(this), target);\n    }\n\n    @Override\n    public void updateOffsetVariables(P partType, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n        this.offsetHandler.updateOffsetVariables(partType, this, network, partNetwork, target);\n    }\n\n    @Nullable\n    @Override\n    public MutableComponent getOffsetVariableError(int slot) {\n        return this.offsetHandler.getOffsetVariableError(slot);\n    }\n\n    @Override\n    public boolean requiresOffsetUpdates() {\n        return this.offsetHandler.offsetVariableEvaluators.stream().anyMatch(InventoryVariableEvaluator::hasVariable);\n    }\n\n    @Override\n    public void markOffsetVariablesChanged() {\n        this.offsetHandler.markOffsetVariablesChanged();\n    }\n\n    @Override\n    public int getMaxOffset() {\n        return maxOffset;\n    }\n\n    @Override\n    public void setMaxOffset(int maxOffset) {\n        this.maxOffset = maxOffset;\n        markDirty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartStateEmpty.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\n/**\n * Dummy part state for parts that should not persist their state.\n * @author rubensworks\n */\npublic class PartStateEmpty<P extends IPartType> extends PartStateBase<P> {\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartStateOffsetHandler.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.MapMaker;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.IntArraySet;\nimport it.unimi.dsi.fastutil.ints.IntSet;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.evaluate.InventoryVariableEvaluator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.part.event.PartVariableDrivenVariableContentsUpdatedEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Handles dynamic offsets inside part states.\n * @author rubensworks\n */\npublic class PartStateOffsetHandler<P extends IPartType> {\n\n    public final List<InventoryVariableEvaluator<ValueTypeInteger.ValueInteger>> offsetVariableEvaluators = Lists.newArrayList();\n    public final Int2ObjectMap<MutableComponent> offsetVariablesSlotMessages = new Int2ObjectArrayMap<>();\n    public boolean offsetVariablesDirty = true;\n    public final IntSet offsetVariableSlotDirty = new IntArraySet();\n    public final Map<IVariable, Boolean> offsetVariableListeners = new MapMaker().weakKeys().makeMap();\n    protected final Int2ObjectMap<IVariable> slotVariables = new Int2ObjectArrayMap<>();\n\n    public void initializeVariableEvaluators(SimpleInventory offsetVariablesInventory, PartTarget target) {\n        offsetVariableEvaluators.clear();\n        for (int i = 0; i < 3; i++) {\n            int slot = i;\n            offsetVariableEvaluators.add(new InventoryVariableEvaluator<>(\n                    offsetVariablesInventory, slot, ValueDeseralizationContext.of(target.getCenter().getPos().getLevel(true)), ValueTypes.INTEGER) {\n                @Override\n                public void onErrorsChanged() {\n                    super.onErrorsChanged();\n                    setOffsetVariableErrors(slot, getErrors());\n                }\n            });\n        }\n    }\n\n    public void updateOffsetVariables(P partType, IPartState<P> partState, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n        // Reload offset variables if needed\n        if (offsetVariablesDirty) {\n            offsetVariablesDirty = false;\n            reloadOffsetVariables(partType, partState, network, partNetwork, target);\n        }\n\n        // Only update single slots if needed\n        if (!offsetVariableSlotDirty.isEmpty()) {\n            IntArraySet offsetVariableSlotDirtyCopy = new IntArraySet(offsetVariableSlotDirty);\n            offsetVariableSlotDirty.clear();\n            for (Integer slot : offsetVariableSlotDirtyCopy) {\n                this.reloadOffsetVariable(partType, partState, network, partNetwork, target, slot);\n            }\n        }\n    }\n\n    public void markOffsetVariablesChanged() {\n        this.offsetVariablesDirty = true;\n    }\n\n    public SimpleInventory getOffsetVariablesInventory(IPartState<P> partState) {\n        SimpleInventory offsetVariablesInventory = new SimpleInventory(3, 1);\n        partState.loadInventoryNamed(\"offsetVariablesInventory\", offsetVariablesInventory);\n        return offsetVariablesInventory;\n    }\n\n    public void reloadOffsetVariables(P partType, IPartState<P> partState, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n        offsetVariableSlotDirty.clear();\n        SimpleInventory offsetVariablesInventory = getOffsetVariablesInventory(partState);\n        initializeVariableEvaluators(offsetVariablesInventory, target);\n        for (int i = 0; i < offsetVariablesInventory.getContainerSize(); i++) {\n            reloadOffsetVariable(partType, partState, network, partNetwork, target, i);\n        }\n    }\n\n    private void setOffsetVariableErrors(int slot, List<MutableComponent> errors) {\n        if (errors.isEmpty()) {\n            if (this.offsetVariablesSlotMessages.size() > slot) {\n                this.offsetVariablesSlotMessages.remove(slot);\n            }\n        } else {\n            this.offsetVariablesSlotMessages.put(slot, errors.get(0));\n        }\n    }\n\n    @Nullable\n    public MutableComponent getOffsetVariableError(int slot) {\n        return this.offsetVariablesSlotMessages.get(slot);\n    }\n\n    protected void reloadOffsetVariable(P partType, IPartState<P> partState, INetwork network, IPartNetwork partNetwork, PartTarget target, int slot) {\n        if (this.offsetVariablesSlotMessages.size() > slot) {\n            this.offsetVariablesSlotMessages.remove(slot);\n        }\n        IVariable lastVariable = slotVariables.get(slot);\n        if (lastVariable != null) {\n            lastVariable.invalidate();\n        }\n\n        InventoryVariableEvaluator<ValueTypeInteger.ValueInteger> evaluator = offsetVariableEvaluators.get(slot);\n        evaluator.refreshVariable(network, false);\n        IVariable<ValueTypeInteger.ValueInteger> variable = evaluator.getVariable(network);\n        if (variable != null) {\n            slotVariables.put(slot, variable);\n            try {\n                // Refresh the recipe if variable is changed\n                // The map is needed because we only want to register the listener once for each variable\n                if (!this.offsetVariableListeners.containsKey(variable)) {\n                    variable.addInvalidationListener(() -> {\n                        this.offsetVariableListeners.remove(variable);\n                        this.offsetVariableSlotDirty.add(slot);\n                    });\n                    this.offsetVariableListeners.put(variable, true);\n                }\n\n                IValue value = variable.getValue();\n                if (value.getType() == ValueTypes.INTEGER) {\n                    int valueRaw = ((ValueTypeInteger.ValueInteger) value).getRawValue();\n                    Vec3i offset = partState.getTargetOffset();\n                    if (slot == 0) {\n                        offset = new Vec3i(valueRaw, offset.getY(), offset.getZ());\n                    }\n                    if (slot == 1) {\n                        offset = new Vec3i(offset.getX(), valueRaw, offset.getZ());\n                    }\n                    if (slot == 2) {\n                        offset = new Vec3i(offset.getX(), offset.getY(), valueRaw);\n                    }\n                    boolean valid = partType.setTargetOffset(partState, target.getCenter(), offset);\n                    if (!valid) {\n                        this.offsetVariablesSlotMessages.put(slot, Component.translatable(\"gui.integrateddynamics.partoffset.slot.message.outofrange\"));\n                        partState.markDirty();\n                    }\n                } else {\n                    this.offsetVariablesSlotMessages.put(slot, Component.translatable(\"gui.integrateddynamics.partoffset.slot.message.noint\"));\n                    partState.markDirty();\n                }\n            } catch (EvaluationException e) {\n                this.offsetVariablesSlotMessages.put(slot, e.getErrorMessage());\n                partState.markDirty();\n            }\n        } else if (evaluator.hasVariable()) {\n            this.offsetVariableSlotDirty.add(slot);\n        }\n\n        try {\n            NeoForge.EVENT_BUS.post(new PartVariableDrivenVariableContentsUpdatedEvent<>(network,\n                    partNetwork, target,\n                    partType, partState, null, variable,\n                    variable != null ? variable.getValue() : null));\n        } catch (EvaluationException e) {\n            // Ignore error\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartTypeAspects.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.Set;\nimport java.util.function.Consumer;\n\n/**\n * An abstract {@link IPartType} that can hold aspects.\n * @author rubensworks\n */\npublic abstract class PartTypeAspects<P extends IPartType<P, S>, S extends IPartState<P>> extends PartTypeConfigurable<P, S> {\n\n    public PartTypeAspects(String name, PartRenderPosition partRenderPosition) {\n        super(name, partRenderPosition);\n    }\n\n    /**\n     * @return All possible aspects that can be used in this part type.\n     */\n    public Set<IAspect> getAspects() {\n        return Aspects.REGISTRY.getAspects(this);\n    }\n\n    @Override\n    public boolean isUpdate(S state) {\n        return !getAspects().isEmpty() || super.isUpdate(state);\n    }\n\n    @Override\n    public int getConsumptionRate(S state) {\n        return 1;\n    }\n\n    @Override\n    public void loadTooltip(ItemStack itemStack, Consumer<Component> tooltipAdder) {\n        super.loadTooltip(itemStack, tooltipAdder);\n        if (getAspects().isEmpty()) {\n            tooltipAdder.accept(Component.translatable(L10NValues.PART_TOOLTIP_NOASPECTS)\n                    .withStyle(ChatFormatting.GOLD));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartTypeBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\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.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPartNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlock;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.item.ItemPart;\nimport org.cyclops.integrateddynamics.core.network.PartNetworkElement;\nimport org.cyclops.integrateddynamics.item.ItemEnhancement;\nimport org.cyclops.integrateddynamics.item.ItemWrench;\n\nimport java.util.*;\nimport java.util.function.Consumer;\n\n/**\n * An abstract {@link IPartType} with a default implementation for creating\n * network elements.\n * @author rubensworks\n */\npublic abstract class PartTypeBase<P extends IPartType<P, S>, S extends IPartState<P>>\n        extends PartTypeAdapter<P, S> {\n\n    private Item item;\n    private Block block;\n    private final String name;\n    private final PartRenderPosition partRenderPosition;\n    private final Map<Class<? extends INetworkEvent>, IEventAction> networkEventActions;\n\n    public Item getItem() {\n        return item;\n    }\n\n    public Block getBlock() {\n        return block;\n    }\n\n    public PartRenderPosition getPartRenderPosition() {\n        return partRenderPosition;\n    }\n\n    public PartTypeBase(String name, PartRenderPosition partRenderPosition) {\n        this.name = name;\n        this.partRenderPosition = partRenderPosition;\n\n        networkEventActions = constructNetworkEventActions();\n\n        registerBlock();\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(getModId(), this.name);\n    }\n\n    protected ModBaseNeoForge getMod() {\n        return IntegratedDynamics._instance;\n    }\n\n    protected String getModId() {\n        return getMod().getModId();\n    }\n\n    /**\n     * Creates and registers a block instance for this part type.\n     * This is mainly used for the block facadeModel.\n     */\n    protected void registerBlock() {\n        BlockConfigCommon blockConfig = new BlockConfigCommon<>(getMod(), \"part_\" + this.name,\n                (eConfig, properties) -> block = createBlock(eConfig, properties),\n                (eConfig, block) -> item  = createItem(eConfig, block)) {\n            @Override\n            public String getFullTranslationKey() {\n                return PartTypeBase.this.getTranslationKey();\n            }\n\n            @Override\n            public Collection<java.util.function.Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n                return Collections.singleton(() -> new ItemStack(getItemInstance()));\n            }\n        };\n        getMod().getConfigHandler().addConfigurable(blockConfig);\n    }\n\n    /**\n     * Factory method for creating a block instance.\n     *\n     * @param blockConfig The config to register the block for.\n     * @param properties\n     * @return The block instance.\n     */\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlock(properties);\n    }\n\n    /**\n     * Factory method for creating a item instance.\n     * @param blockConfig The block config to register the item for.\n     * @param block The block corresponding to the item.\n     * @return The item instance.\n     */\n    protected Item createItem(BlockConfigCommon<?> blockConfig, Block block) {\n        return new ItemPart<>(blockConfig.createDefaultItemProperties()\n                .overrideDescription(this.getTranslationKey()), this);\n    }\n\n    @Override\n    public Identifier getBlockModelPath() {\n        return Identifier.fromNamespaceAndPath(getModId(), \"part_\" + this.name);\n    }\n\n    @Override\n    protected String createTranslationKey() {\n        return \"parttype.\" + getModId() + \".\" + this.name;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public INetworkElement createNetworkElement(IPartContainer partContainer, DimPos pos, Direction side) {\n        return new PartNetworkElement(this, PartPos.of(pos, side));\n    }\n\n    @Override\n    public InteractionResult onPartActivated(S partState, BlockPos pos, Level world, Player player, InteractionHand hand,\n                                            ItemStack heldItem, BlockHitResult hit) {\n        // Drop through if the player is sneaking\n        if(player.isSecondaryUseActive()) {\n            return InteractionResult.PASS;\n        }\n\n        // Consume enhancement\n        if (heldItem.getItem() instanceof ItemEnhancement itemEnhancement) {\n            InteractionResult result = itemEnhancement.applyEnhancement(this, partState, heldItem, player, hand);\n            if (result.consumesAction()) {\n                return result;\n            }\n        }\n\n        // Set offset and side\n        PartPos partPos = PartPos.of(world, pos, hit.getDirection());\n        if (heldItem.getItem() instanceof ItemWrench itemWrench) {\n            InteractionResult result = itemWrench.performPartAction(hit, this, partState, heldItem, player, hand, partPos);\n            if (result.consumesAction()) {\n                return result;\n            }\n        }\n\n        if(getContainerProvider(partPos).isPresent()) {\n            if (!world.isClientSide()) {\n                return PartHelpers.openContainerPart((ServerPlayer) player, partPos, this);\n            }\n            return InteractionResult.SUCCESS;\n        }\n        return InteractionResult.PASS;\n    }\n\n    @Override\n    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n        super.addDrops(target, state, itemStacks, dropMainElement, saveState);\n\n        // Save enhancements\n        if (!saveState && state.getMaxOffset() > 0) {\n            // Drop Part Offset items each with as maximum the GeneralConfig.enchancementOffsetPartDropValue offset value.\n            int remainingOffset = state.getMaxOffset();\n            while (remainingOffset > 0) {\n                int offset;\n                if (remainingOffset < GeneralConfig.enchancementOffsetPartDropValue) {\n                    offset = remainingOffset;\n                } else {\n                    offset = GeneralConfig.enchancementOffsetPartDropValue;\n                }\n                remainingOffset -= offset;\n\n                ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_ENHANCEMENT_OFFSET);\n                RegistryEntries.ITEM_ENHANCEMENT_OFFSET.get().setEnhancementValue(itemStack, offset);\n                itemStacks.add(itemStack);\n            }\n            state.setMaxOffset(0);\n        }\n    }\n\n    @Override\n    public BlockState getBlockState(IPartContainer partContainer, Direction side) {\n        return getBlock().defaultBlockState()\n                .setValue(IgnoredBlock.FACING, side);\n    }\n\n    @Override\n    public BlockState getBaseBlockState() {\n        return getBlock().defaultBlockState();\n    }\n\n    @Override\n    public void loadTooltip(S state, List<Component> lines) {\n        if(!state.isEnabled()) {\n            lines.add(Component.translatable(L10NValues.PART_TOOLTIP_DISABLED));\n        }\n        lines.add(Component.translatable(L10NValues.GENERAL_ITEM_ID, state.getId()));\n\n        if (state.getMaxOffset() > 0) {\n            lines.add(Component.translatable(L10NValues.PART_TOOLTIP_MAXOFFSET, state.getMaxOffset()));\n        }\n    }\n\n    @Override\n    public void loadTooltip(ItemStack itemStack, Consumer<Component> tooltipAdder) {\n        if(itemStack.has(RegistryEntries.DATACOMPONENT_PART_STATE)) {\n            CompoundTag tag = itemStack.get(RegistryEntries.DATACOMPONENT_PART_STATE);\n            if(tag.contains(\"id\")) {\n                int id = tag.getInt(\"id\").orElseThrow();\n                tooltipAdder.accept(Component.translatable(L10NValues.GENERAL_ITEM_ID, id));\n            }\n            if(tag.contains(\"maxOffset\")) {\n                int maxOffset = tag.getInt(\"maxOffset\").orElseThrow();\n                tooltipAdder.accept(Component.translatable(L10NValues.PART_TOOLTIP_MAXOFFSET, maxOffset));\n            }\n        }\n\n        super.loadTooltip(itemStack, tooltipAdder);\n    }\n\n    /**\n     * Override this to register your network event actions.\n     * @return The event actions.\n     */\n    protected Map<Class<? extends INetworkEvent>, IEventAction> constructNetworkEventActions() {\n        return new IdentityHashMap<>();\n    }\n\n    @Override\n    public final boolean hasEventSubscriptions() {\n        return !networkEventActions.isEmpty();\n    }\n\n    @Override\n    public final Set<Class<? extends INetworkEvent>> getSubscribedEvents() {\n        return networkEventActions.keySet();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public final void onEvent(INetworkEvent event, IPartNetworkElement<P, S> networkElement) {\n        if (networkElement.getTarget().getCenter().getPos().isLoaded()) {\n            networkEventActions.get(event.getClass()).onAction(event.getNetwork(), networkElement.getTarget(),\n                    networkElement.getPartState(), event);\n        }\n    }\n\n    @Override\n    public boolean forceLightTransparency(S state) {\n        return false;\n    }\n\n    @Override\n    public void writeExtraGuiData(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n        packetBuffer.writeUtf(this.getUniqueName().toString());\n    }\n\n    public interface IEventAction<P extends IPartType<P, S>, S extends IPartState<P>, E extends INetworkEvent> {\n\n        public void onAction(INetwork network, PartTarget target, S state, E event);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartTypeConfigurable.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartOffset;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerPartSettings;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * An abstract {@link IPartType} that can have settings.\n * @author rubensworks\n */\npublic abstract class PartTypeConfigurable<P extends IPartType<P, S>, S extends IPartState<P>> extends PartTypeBase<P, S> {\n\n    public PartTypeConfigurable(String name, PartRenderPosition partRenderPosition) {\n        super(name, partRenderPosition);\n    }\n\n    @Override\n    public Optional<MenuProvider> getContainerProviderSettings(PartPos pos) {\n        return Optional.of(new MenuProvider() {\n\n            @Override\n            public Component getDisplayName() {\n                return Component.translatable(getTranslationKey());\n            }\n\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);\n                return new ContainerPartSettings(id, playerInventory, new SimpleContainer(0),\n                        data.getRight(), Optional.of(data.getLeft()), data.getMiddle());\n            }\n\n            @Override\n            public boolean shouldTriggerClientSideContainerClosingOnOpen() {\n                return false;\n            }\n        });\n    }\n\n    @Override\n    public void writeExtraGuiDataSettings(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n        PacketCodecs.write(packetBuffer, pos);\n        packetBuffer.writeUtf(this.getUniqueName().toString());\n    }\n\n    @Override\n    public Optional<MenuProvider> getContainerProviderOffsets(PartPos pos) {\n        return Optional.of(new MenuProvider() {\n\n            @Override\n            public Component getDisplayName() {\n                return Component.translatable(getTranslationKey());\n            }\n\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);\n                return new ContainerPartOffset(id, playerInventory, new SimpleContainer(0),\n                        data.getRight(), Optional.of(data.getLeft()), data.getMiddle());\n            }\n\n            @Override\n            public boolean shouldTriggerClientSideContainerClosingOnOpen() {\n                return false;\n            }\n        });\n    }\n\n    @Override\n    public void writeExtraGuiDataOffsets(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n        PacketCodecs.write(packetBuffer, pos);\n        packetBuffer.writeUtf(this.getUniqueName().toString());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartTypeRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.IPartTypeRegistry;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * Registry for {@link IPartType}.\n * @author rubensworks\n */\npublic final class PartTypeRegistry implements IPartTypeRegistry {\n\n    private static PartTypeRegistry INSTANCE = new PartTypeRegistry();\n\n    private Map<String, IPartType> partTypes = Maps.newHashMap();\n\n    private PartTypeRegistry() {\n\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static PartTypeRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> P register(P partType) {\n        if(partTypes.containsKey(partType.getUniqueName())) {\n            throw new DuplicatePartTypeException(String.format(\"Tried to register a part type %s with name %s while \" +\n                    \"the registry already container %s for that name.\", partType, partType.getUniqueName(),\n                    partTypes.get(partType.getUniqueName())));\n        }\n        partTypes.put(partType.getUniqueName().toString(), partType);\n        return partType;\n    }\n\n    @Override\n    public Collection<IPartType> getPartTypes() {\n        return partTypes.values();\n    }\n\n    @Override\n    public IPartType getPartType(Identifier partName) {\n        return partTypes.get(partName.toString());\n    }\n\n    public static class DuplicatePartTypeException extends RuntimeException {\n\n        public DuplicatePartTypeException(String msg) {\n            super(msg);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/PartTypes.java",
    "content": "package org.cyclops.integrateddynamics.core.part;\n\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.part.IPartTypeRegistry;\nimport org.cyclops.integrateddynamics.part.*;\n\n/**\n * Collection of parts types.\n * @author rubensworks\n */\npublic final class PartTypes {\n\n    public static final IPartTypeRegistry REGISTRY = IntegratedDynamics._instance.getRegistryManager().getRegistry(IPartTypeRegistry.class);\n\n    // Readers\n    public static final PartTypeAudioReader AUDIO_READER = new PartTypeAudioReader(\"audio_reader\");\n    public static final PartTypeBlockReader BLOCK_READER = new PartTypeBlockReader(\"block_reader\");\n    public static final PartTypeEntityReader ENTITY_READER = new PartTypeEntityReader(\"entity_reader\");\n    public static final PartTypeExtraDimensionalReader EXTRADIMENSIONAL_READER = new PartTypeExtraDimensionalReader(\"extradimensional_reader\");\n    public static final PartTypeFluidReader FLUID_READER = new PartTypeFluidReader(\"fluid_reader\");\n    public static final PartTypeInventoryReader INVENTORY_READER = new PartTypeInventoryReader(\"inventory_reader\");\n    public static final PartTypeMachineReader MACHINE_READER = new PartTypeMachineReader(\"machine_reader\");\n    public static final PartTypeNetworkReader NETWORK_READER = new PartTypeNetworkReader(\"network_reader\");\n    public static final PartTypeRedstoneReader REDSTONE_READER = new PartTypeRedstoneReader(\"redstone_reader\");\n    public static final PartTypeWorldReader WORLD_READER = new PartTypeWorldReader(\"world_reader\");\n\n    // Writers\n    public static final PartTypeAudioWriter AUDIO_WRITER = new PartTypeAudioWriter(\"audio_writer\");\n    public static final PartTypeEffectWriter EFFECT_WRITER = new PartTypeEffectWriter(\"effect_writer\");\n    public static final PartTypeEntityWriter ENTITY_WRITER = new PartTypeEntityWriter(\"entity_writer\");\n    public static final PartTypeMachineWriter MACHINE_WRITER = new PartTypeMachineWriter(\"machine_writer\");\n    public static final PartTypeInventoryWriter INVENTORY_WRITER = new PartTypeInventoryWriter(\"inventory_writer\");\n    public static final PartTypeRedstoneWriter REDSTONE_WRITER = new PartTypeRedstoneWriter(\"redstone_writer\");\n\n    // Panels\n    public static final PartTypePanelLightStatic STATIC_LIGHT_PANEL = new PartTypePanelLightStatic(\"static_light_panel\");\n    public static final PartTypePanelLightDynamic DYNAMIC_LIGHT_PANEL = new PartTypePanelLightDynamic(\"dynamic_light_panel\");\n    public static final PartTypePanelDisplay DISPLAY_PANEL = new PartTypePanelDisplay(\"display_panel\");\n\n    // Connectors\n    public static final PartTypeConnectorMonoDirectional CONNECTOR_MONO = new PartTypeConnectorMonoDirectional(\"connector_mono_directional\");\n    public static final PartTypeConnectorOmniDirectional CONNECTOR_OMNI = new PartTypeConnectorOmniDirectional(\"connector_omni_directional\");\n\n    public static void load() {}\n\n    public static void register() {\n        REGISTRY.register(AUDIO_READER);\n        REGISTRY.register(BLOCK_READER);\n        REGISTRY.register(ENTITY_READER);\n        REGISTRY.register(EXTRADIMENSIONAL_READER);\n        REGISTRY.register(FLUID_READER);\n        REGISTRY.register(INVENTORY_READER);\n        REGISTRY.register(MACHINE_READER);\n        REGISTRY.register(NETWORK_READER);\n        REGISTRY.register(REDSTONE_READER);\n        REGISTRY.register(WORLD_READER);\n\n        REGISTRY.register(AUDIO_WRITER);\n        REGISTRY.register(EFFECT_WRITER);\n        REGISTRY.register(ENTITY_WRITER);\n        REGISTRY.register(MACHINE_WRITER);\n        REGISTRY.register(INVENTORY_WRITER);\n        REGISTRY.register(REDSTONE_WRITER);\n\n        REGISTRY.register(STATIC_LIGHT_PANEL);\n        REGISTRY.register(DYNAMIC_LIGHT_PANEL);\n        REGISTRY.register(DISPLAY_PANEL);\n\n        REGISTRY.register(CONNECTOR_MONO);\n        REGISTRY.register(CONNECTOR_OMNI);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/AspectRegistry.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect;\n\nimport com.google.common.collect.ImmutableSet;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.ValuePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariableFacadePredicate;\nimport org.cyclops.integrateddynamics.api.advancement.criterion.VariablePredicate;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IAspectVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.*;\nimport org.cyclops.integrateddynamics.core.item.AspectVariableFacade;\n\nimport java.util.*;\n\n/**\n * Registry for {@link IAspect}.\n * @author rubensworks\n */\npublic final class AspectRegistry implements IAspectRegistry {\n\n    private static AspectRegistry INSTANCE = new AspectRegistry();\n    private static final IAspectVariableFacade INVALID_FACADE = new AspectVariableFacade(false, -1, null);\n\n    private final Map<IPartType, Set<IAspect<?, ?>>> partAspects = new IdentityHashMap<>();\n    private final Map<IPartType, Set<IAspectRead<?, ?>>> partReadAspects = new IdentityHashMap<>();\n    private final Map<IPartType, Set<IAspectWrite<?, ?>>> partWriteAspects = new IdentityHashMap<>();\n    private final Map<IPartType, List<IAspectRead<?, ?>>> partReadAspectsListTransform = new IdentityHashMap<>();\n    private final Map<IPartType, List<IAspectWrite<?, ?>>> partWriteAspectsListTransform = new IdentityHashMap<>();\n    private final Map<String, IAspect<?, ?>> unlocalizedAspects = Maps.newHashMap();\n    private final Map<String, IAspectRead<?, ?>> unlocalizedReadAspects = Maps.newHashMap();\n    private final Map<String, IAspectWrite<?, ?>> unlocalizedWriteAspects = Maps.newHashMap();\n\n    private AspectRegistryClient client;\n\n    private AspectRegistry() {\n        IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class).registerHandler(this);\n        if(IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            client = new AspectRegistryClient();\n        }\n    }\n\n    /**\n     * @return The unique instance.\n     */\n    public static AspectRegistry getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public AspectRegistryClient getClient() {\n        return client;\n    }\n\n    @Override\n    public IAspect register(IPartType partType, IAspect aspect) {\n        registerSubAspectType(partType, aspect, partAspects, unlocalizedAspects);\n        if(aspect instanceof IAspectRead) {\n            registerSubAspectType(partType, (IAspectRead) aspect, partReadAspects, unlocalizedReadAspects);\n            partReadAspectsListTransform.put(partType, Lists.newArrayList(partReadAspects.get(partType)));\n        }\n        if(aspect instanceof IAspectWrite) {\n            registerSubAspectType(partType, (IAspectWrite) aspect, partWriteAspects, unlocalizedWriteAspects);\n            partWriteAspectsListTransform.put(partType, Lists.newArrayList(partWriteAspects.get(partType)));\n        }\n        return aspect;\n    }\n\n    protected <T extends IAspect<?, ?>> void registerSubAspectType(IPartType partType, T aspect, Map<IPartType,\n                                                             Set<T>> partAspects, Map<String, T> unlocalizedAspects) {\n        Set<T> aspects = partAspects.get(partType);\n        if(aspects == null) {\n            aspects = Sets.newLinkedHashSet();\n            partAspects.put(partType, aspects);\n        }\n        aspects.add(aspect);\n        unlocalizedAspects.put(aspect.getUniqueName().toString(), aspect);\n    }\n\n    @Override\n    public void register(IPartType partType, Collection<IAspect> aspects) {\n        for(IAspect aspect : aspects) {\n            register(partType, aspect);\n        }\n    }\n\n    @Override\n    public Set<IAspect> getAspects(IPartType partType) {\n        Set<IAspect<?, ?>> aspects = partAspects.get(partType);\n        if(aspects == null) {\n            return Collections.emptySet();\n        }\n        return Collections.unmodifiableSet(aspects);\n    }\n\n    @Override\n    public List<IAspectRead<?, ?>> getReadAspects(IPartType partType) {\n        List<IAspectRead<?, ?>> aspects = partReadAspectsListTransform.get(partType);\n        if (aspects == null) {\n            return Collections.emptyList();\n        }\n        return Collections.unmodifiableList(aspects);\n    }\n\n    @Override\n    public List<IAspectWrite<?, ?>> getWriteAspects(IPartType partType) {\n        List<IAspectWrite<?, ?>> aspects = partWriteAspectsListTransform.get(partType);\n        if (aspects == null) {\n            return Collections.emptyList();\n        }\n        return Collections.unmodifiableList(aspects);\n    }\n\n    @Override\n    public Set<IAspect> getAspects() {\n        return ImmutableSet.copyOf(unlocalizedAspects.values());\n    }\n\n    @Override\n    public Set<IAspectRead> getReadAspects() {\n        return ImmutableSet.copyOf(unlocalizedReadAspects.values());\n    }\n\n    @Override\n    public Set<IAspectWrite> getWriteAspects() {\n        return ImmutableSet.copyOf(unlocalizedWriteAspects.values());\n    }\n\n    @Override\n    public IAspect getAspect(Identifier name) {\n        return unlocalizedAspects.get(name.toString());\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"aspect\");\n    }\n\n    @Override\n    public IAspectVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, int id, CompoundTag tag) {\n        if(!(tag.contains(\"partId\") || tag.contains(\"partId\")) || !tag.contains(\"aspectName\")) {\n            return INVALID_FACADE;\n        }\n        int partId = tag.getInt(\"partId\").orElseThrow();\n        IAspect aspect = getAspect(Identifier.parse(tag.getString(\"aspectName\").orElseThrow()));\n        if(aspect == null) {\n            return INVALID_FACADE;\n        }\n        return new AspectVariableFacade(id, partId, aspect);\n    }\n\n    @Override\n    public void setVariableFacade(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag, IAspectVariableFacade variableFacade) {\n        tag.putInt(\"partId\", variableFacade.getPartId());\n        tag.putString(\"aspectName\", variableFacade.getAspect().getUniqueName().toString());\n    }\n\n    @Override\n    public boolean isInstance(IVariableFacade variableFacade) {\n        return variableFacade instanceof IAspectVariableFacade;\n    }\n\n    @Override\n    public boolean isInstance(IVariable<?> variable) {\n        return variable instanceof IAspectVariable;\n    }\n\n    public static class AspectVariablePredicate extends VariablePredicate<IAspectVariable> {\n\n        private final Optional<IAspect> aspect;\n\n        public AspectVariablePredicate(Optional<IValueType> valueType, Optional<ValuePredicate> valuePredicate, Optional<IAspect> aspect) {\n            super(IAspectVariable.class, valueType, valuePredicate);\n            this.aspect = aspect;\n        }\n\n        public Optional<IAspect> getAspect() {\n            return aspect;\n        }\n\n        @Override\n        protected boolean testTyped(IAspectVariable variable) {\n            return super.testTyped(variable) && (aspect.isEmpty() || variable.getAspect() == aspect.get());\n        }\n    }\n\n    public static class AspectVariableFacadePredicate extends VariableFacadePredicate<IAspectVariableFacade> {\n\n        private final Optional<IAspect> aspect;\n\n        public AspectVariableFacadePredicate(Optional<IAspect> aspect) {\n            super(IAspectVariableFacade.class);\n            this.aspect = aspect;\n        }\n\n        public Optional<IAspect> getAspect() {\n            return aspect;\n        }\n\n        @Override\n        protected boolean testTyped(IAspectVariableFacade variableFacade) {\n            return super.testTyped(variableFacade) && (aspect.isEmpty() || variableFacade.getAspect() == aspect.get());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/AspectRegistryClient.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect;\n\nimport net.minecraft.client.color.item.Constant;\nimport net.minecraft.client.renderer.item.CuboidItemModelWrapper;\nimport net.minecraft.client.renderer.item.ItemModel;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRegistryClient;\n\nimport java.util.*;\n\n/**\n * @author rubensworks\n */\npublic class AspectRegistryClient implements IAspectRegistryClient {\n\n    private Map<IAspect, ItemModel.Unbaked> aspectModels = new IdentityHashMap<>();;\n\n    @Override\n    public void registerAspectModel(IAspect aspect, Identifier modelLocation) {\n        aspectModels.put(aspect, new CuboidItemModelWrapper.Unbaked(modelLocation, java.util.Optional.empty(), List.of(new Constant(-1))));\n    }\n\n    @Override\n    public ItemModel.Unbaked getAspectModel(IAspect aspect) {\n        return aspectModels.get(aspect);\n    }\n\n    @Override\n    public Collection<ItemModel.Unbaked> getAspectModels() {\n        return Collections.unmodifiableCollection(aspectModels.values());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/LazyAspectVariable.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect;\n\nimport net.minecraft.network.chat.Component;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.VariableAdapter;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.function.Supplier;\n\n/**\n * Variable for a specific aspect from a part that calculates its target value only maximum once per ticking interval.\n * No calculations will be done if the value of this variable is not called.\n * @author rubensworks\n */\npublic abstract class LazyAspectVariable<V extends IValue> extends VariableAdapter<V> implements IAspectVariable<V> {\n\n    private final IValueType<V> type;\n    private final Supplier<PartTarget> targetSupplier;\n    private final IAspectRead<V, ?> aspect;\n    private V value;\n    private IAspectProperties cachedProperties = null;\n\n    private boolean isGettingValue = false;\n\n    public LazyAspectVariable(IValueType<V> type, Supplier<PartTarget> targetSupplier, IAspectRead<V, ?> aspect) {\n        this.type = type;\n        this.targetSupplier = targetSupplier;\n        this.aspect = aspect;\n    }\n\n    public IValueType<V> getType() {\n        return type;\n    }\n\n    public IAspectRead<V, ?> getAspect() {\n        return aspect;\n    }\n\n    public PartTarget getTarget() {\n        return targetSupplier.get();\n    }\n\n    @Override\n    public void invalidate() {\n        if (value != null) {\n            value = null;\n            cachedProperties = null;\n            super.invalidate();\n        }\n    }\n\n    @Override\n    public V getValue() throws EvaluationException {\n        if(value == null) {\n            if(this.isGettingValue) {\n                throw new EvaluationException(Component.translatable(L10NValues.VARIABLE_ERROR_RECURSION,\n                        Component.translatable(getAspect().getTranslationKey())));\n            }\n            this.isGettingValue = true;\n            try {\n                this.value = getValueLazy();\n            } catch (EvaluationException e) {\n                this.isGettingValue = false;\n                throw e;\n            }\n            this.isGettingValue = false;\n        }\n        return this.value;\n    }\n\n    protected IAspectProperties getAspectProperties() {\n        if(cachedProperties == null && getAspect().hasProperties()) {\n            PartPos pos = getTarget().getCenter();\n            Pair<IPartType, IPartState> partData = PartPos.getPartData(pos);\n            if (partData != null) {\n                cachedProperties = getAspect().getProperties(partData.getLeft(), getTarget(), partData.getRight());\n            }\n        }\n        return cachedProperties;\n    }\n\n    /**\n     * Calculate the current value for this variable.\n     * It will only be called when required.\n     * @return The current value of this variable.\n     * @throws EvaluationException If evaluation has gone wrong.\n     */\n    public abstract V getValueLazy() throws EvaluationException;\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/UpdatingAspectVariable.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\n\nimport java.util.Objects;\n\n/**\n * Variable for a specific aspect from a part that requires updates for value changes.\n * @author rubensworks\n */\npublic abstract class UpdatingAspectVariable<V extends IValue> implements IAspectVariable<V> {\n\n    private final IValueType<V> type;\n    private final PartTarget target;\n    private V value;\n\n    public UpdatingAspectVariable(IValueType<V> type, PartTarget target) {\n        this.type = type;\n        this.target = target;\n        this.value = type.getDefault();\n    }\n\n    public IValueType<V> getType() {\n        return type;\n    }\n\n    public PartTarget getTarget() {\n        return target;\n    }\n\n    public V getValue() {\n        return value;\n    }\n\n    public void setValue(V value) {\n        this.value = Objects.requireNonNull(value, \"value is marked non-null but is null\");\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        UpdatingAspectVariable<?> that = (UpdatingAspectVariable<?>) o;\n        return Objects.equals(type, that.type) && Objects.equals(target, that.target) && Objects.equals(value, that.value);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(type, target, value);\n    }\n\n    @Override\n    public String toString() {\n        return \"UpdatingAspectVariable{\" +\n                \"type=\" + type +\n                \", target=\" + target +\n                \", value=\" + value +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/build/AspectBuilder.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.build;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Lists;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.AspectUpdateType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.part.aspect.read.AspectReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.write.AspectWriteBase;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Immutable builder for aspects.\n * @param <V> The value type.\n * @param <T> The value type type.\n * @param <O> The current output type for value handling.\n * @author rubensworks\n */\npublic class AspectBuilder<V extends IValue, T extends IValueType<V>, O> {\n\n    private final boolean read;\n    private final T valueType;\n    private final List<String> kinds;\n    private final IAspectProperties defaultAspectProperties;\n    private final List<IAspectValuePropagator> valuePropagators;\n    private final List<IAspectWriteActivator> writeActivators;\n    private final List<IAspectWriteDeactivator> writeDeactivators;\n    private final String modId;\n    private final List<IAspectUpdateListener.Before> beforeUpdateListeners;\n    private final List<IAspectUpdateListener.After> afterUpdateListeners;\n    private final AspectUpdateType updateType;\n\n    private AspectBuilder(boolean read, T valueType, List<String> kinds, IAspectProperties defaultAspectProperties,\n                          List<IAspectValuePropagator> valuePropagators, List<IAspectWriteActivator> writeActivators,\n                          List<IAspectWriteDeactivator> writeDeactivators, String modId,\n                          List<IAspectUpdateListener.Before> beforeUpdateListeners, List<IAspectUpdateListener.After> afterUpdateListeners,\n                          AspectUpdateType updateType) {\n        this.read = read;\n        this.valueType = valueType;\n        this.kinds = kinds;\n        this.defaultAspectProperties = defaultAspectProperties;\n        this.valuePropagators = valuePropagators;\n        this.writeActivators = writeActivators;\n        this.writeDeactivators = writeDeactivators;\n        this.modId = Objects.requireNonNull(modId);\n        this.beforeUpdateListeners = beforeUpdateListeners;\n        this.afterUpdateListeners = afterUpdateListeners;\n        this.updateType = updateType;\n    }\n\n    /**\n     * Add the given value propagator.\n     * @param valuePropagator The value propagator.\n     * @param <O2> The new output type.\n     * @return The new builder instance.\n     */\n    public <O2> AspectBuilder<V, T, O2> handle(IAspectValuePropagator<O, O2> valuePropagator) {\n        return handle(valuePropagator, null);\n    }\n\n    /**\n     * Add the given value propagator.\n     * @param valuePropagator The value propagator.\n     * @param kind The kind to append.\n     * @param <O2> The new output type.\n     * @return The new builder instance.\n     */\n    public <O2> AspectBuilder<V, T, O2> handle(IAspectValuePropagator<O, O2> valuePropagator, String kind) {\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, kind),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, valuePropagator),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * Add the given kind.\n     * @param kind The kind to append.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> appendKind(String kind) {\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, kind),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * Set the given default aspect properties.\n     * @param aspectProperties The aspect properties.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> withProperties(IAspectProperties aspectProperties) {\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                aspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * Add the given aspect activator.\n     * Only applicable for writers.\n     * @param activator The aspect activator callback.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> appendActivator(IAspectWriteActivator activator) {\n        if(this.read) {\n            throw new RuntimeException(\"Activators are only applicable for writers.\");\n        }\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, activator),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * Add the given aspect deactivator.\n     * Only applicable for writers.\n     * @param deactivator The aspect deactivator callback.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> appendDeactivator(IAspectWriteDeactivator deactivator) {\n        if(this.read) {\n            throw new RuntimeException(\"Deactivators are only applicable for writers.\");\n        }\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, deactivator),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * Set the mod that provides the aspect.\n     * @param modId The mod id.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> byMod(String modId) {\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * Add a before-update listener.\n     * @param listener The listener.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> appendBeforeUpdateListener(IAspectUpdateListener.Before listener) {\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                Helpers.joinList(beforeUpdateListeners, listener),\n                Helpers.joinList(afterUpdateListeners, null),\n                updateType);\n    }\n\n    /**\n     * Add an after-update listener.\n     * @param listener The listener.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> appendAfterUpdateListener(IAspectUpdateListener.After listener) {\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                Helpers.joinList(beforeUpdateListeners, null),\n                Helpers.joinList(afterUpdateListeners, listener),\n                updateType);\n    }\n\n    /**\n     * Set the update type of the reader aspect.\n     * @param updateType The update type.\n     * @return The new builder instance.\n     */\n    public AspectBuilder<V, T, O> withUpdateType(AspectUpdateType updateType) {\n        if(!this.read) {\n            throw new RuntimeException(\"Custom update types are only applicable to readers.\");\n        }\n        return new AspectBuilder<>(\n                this.read, this.valueType,\n                Helpers.joinList(this.kinds, null),\n                this.defaultAspectProperties,\n                Helpers.joinList(this.valuePropagators, null),\n                Helpers.joinList(writeActivators, null),\n                Helpers.joinList(writeDeactivators, null),\n                modId,\n                beforeUpdateListeners,\n                afterUpdateListeners,\n                updateType);\n    }\n\n    /**\n     * @return The built read aspect.\n     */\n    public IAspectRead<V, T> buildRead() {\n        if(!this.read) {\n            throw new RuntimeException(\"Tried to build a reader from a writer builder\");\n        }\n        return new BuiltReader<V, T>((AspectBuilder<V, T, V>) this);\n    }\n\n    /**\n     * @return The built write aspect.\n     */\n    public IAspectWrite<V, T> buildWrite() {\n        if(this.read) {\n            throw new RuntimeException(\"Tried to build a writer from a reader builder\");\n        }\n        return new BuiltWriter<V, T>((AspectBuilder<V, T, V>) this);\n    }\n\n    /**\n     * Create a new read builder for the given value type.\n     * @param valueType The value type the eventual built aspect will output.\n     * @param <V> The value type.\n     * @param <T> The value type type.\n     * @return The builder instance.\n     */\n    public static <V extends IValue, T extends IValueType<V>> AspectBuilder<V, T, Pair<PartTarget, IAspectProperties>> forReadType(T valueType) {\n        return new AspectBuilder<>(true, valueType, ImmutableList.of(valueType.getTypeName()), null,\n                Collections.<IAspectValuePropagator>emptyList(), Collections.<IAspectWriteActivator>emptyList(),\n                Collections.<IAspectWriteDeactivator>emptyList(), IntegratedDynamics._instance.getModId(), Lists.newArrayList(), Lists.newArrayList(), AspectUpdateType.NETWORK_TICK);\n    }\n\n    /**\n     * Create a new write builder for the given value type.\n     * @param valueType The value type the eventual built aspect expects.\n     * @param <V> The value type.\n     * @param <T> The value type type.\n     * @return The builder instance.\n     */\n    public static <V extends IValue, T extends IValueType<V>> AspectBuilder<V, T, Triple<PartTarget, IAspectProperties, IVariable<V>>> forWriteType(T valueType) {\n        return new AspectBuilder<>(false, valueType, ImmutableList.of(valueType.getTypeName()), null,\n                Collections.<IAspectValuePropagator>emptyList(), Collections.<IAspectWriteActivator>emptyList(),\n                Collections.<IAspectWriteDeactivator>emptyList(), IntegratedDynamics._instance.getModId(), Lists.newArrayList(), Lists.newArrayList(), AspectUpdateType.NETWORK_TICK);\n    }\n\n    private static class BuiltReader<V extends IValue, T extends IValueType<V>> extends AspectReadBase<V, T> {\n\n        private final T valueType;\n        private final List<IAspectValuePropagator> valuePropagators;\n        private final List<IAspectUpdateListener.Before> beforeUpdateListeners;\n        private final List<IAspectUpdateListener.After> afterUpdateListeners;\n\n        public BuiltReader(AspectBuilder<V, T, V> aspectBuilder) {\n            super(aspectBuilder.modId, deriveUnlocalizedType(aspectBuilder), aspectBuilder.defaultAspectProperties,\n                    aspectBuilder.updateType);\n            this.valueType = aspectBuilder.valueType;\n            this.valuePropagators = aspectBuilder.valuePropagators;\n            this.beforeUpdateListeners = aspectBuilder.beforeUpdateListeners;\n            this.afterUpdateListeners = aspectBuilder.afterUpdateListeners;\n        }\n\n        protected static <V extends IValue, T extends IValueType<V>> String deriveUnlocalizedType(AspectBuilder<V, T, V> aspectBuilder) {\n            StringBuilder sb = new StringBuilder();\n            for(String kind : aspectBuilder.kinds) {\n                sb.append(\".\");\n                sb.append(kind);\n            }\n            return sb.toString();\n        }\n\n        @Override\n        protected V getValue(PartTarget target, IAspectProperties properties) throws EvaluationException {\n            Object output = Pair.of(target, properties);\n            for(IAspectValuePropagator valuePropagator : valuePropagators) {\n                output = valuePropagator.getOutput(output);\n            }\n            return (V) output;\n        }\n\n        @Override\n        public T getValueType() {\n            return valueType;\n        }\n\n        @Override\n        public <P extends IPartType<P, S>, S extends IPartState<P>> void update(INetwork network, IPartNetwork partNetwork, P partType, PartTarget target, S state) {\n            this.beforeUpdateListeners.forEach(l -> l.onUpdate(network, partNetwork, partType, target, state));\n            super.update(network, partNetwork, partType, target, state);\n            this.afterUpdateListeners.forEach(l -> l.onUpdate(network, partNetwork, partType, target, state));\n        }\n    }\n\n    private static class BuiltWriter<V extends IValue, T extends IValueType<V>> extends AspectWriteBase<V, T> {\n\n        private final T valueType;\n        private final List<IAspectValuePropagator> valuePropagators;\n        private final List<IAspectWriteActivator> writeActivators;\n        private final List<IAspectWriteDeactivator> writeDeactivators;\n        private final List<IAspectUpdateListener.Before> beforeUpdateListeners;\n        private final List<IAspectUpdateListener.After> afterUpdateListeners;\n\n        public BuiltWriter(AspectBuilder<V, T, V> aspectBuilder) {\n            super(aspectBuilder.modId, deriveUnlocalizedType(aspectBuilder), aspectBuilder.defaultAspectProperties);\n            this.valueType = aspectBuilder.valueType;\n            this.valuePropagators = aspectBuilder.valuePropagators;\n            this.writeActivators = aspectBuilder.writeActivators;\n            this.writeDeactivators = aspectBuilder.writeDeactivators;\n            this.beforeUpdateListeners = aspectBuilder.beforeUpdateListeners;\n            this.afterUpdateListeners = aspectBuilder.afterUpdateListeners;\n        }\n\n        protected static <V extends IValue, T extends IValueType<V>> String deriveUnlocalizedType(AspectBuilder<V, T, V> aspectBuilder) {\n            StringBuilder sb = new StringBuilder();\n            for(String kind : aspectBuilder.kinds) {\n                sb.append(\".\");\n                sb.append(kind);\n            }\n            return sb.toString();\n        }\n\n        @Override\n        public T getValueType() {\n            return valueType;\n        }\n\n        @Override\n        public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void write(P partType, PartTarget target, S state, IVariable<V> variable) throws EvaluationException {\n            IAspectProperties properties = hasProperties() ? getProperties(partType, target, state) : null;\n            Object output = Triple.of(target, properties, variable);\n            for(IAspectValuePropagator valuePropagator : valuePropagators) {\n                output = valuePropagator.getOutput(output);\n            }\n        }\n\n        @Override\n        public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onActivate(P partType, PartTarget target, S state) {\n            super.onActivate(partType, target, state);\n            for (IAspectWriteActivator writeActivator : this.writeActivators) {\n                writeActivator.onActivate(partType, target, state);\n            }\n        }\n\n        @Override\n        public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onDeactivate(P partType, PartTarget target, S state) {\n            super.onDeactivate(partType, target, state);\n            for (IAspectWriteDeactivator writeDeactivator : this.writeDeactivators) {\n                writeDeactivator.onDeactivate(partType, target, state);\n            }\n        }\n\n        @Override\n        public <P extends IPartType<P, S>, S extends IPartState<P>> void update(INetwork network, IPartNetwork partNetwork, P partType, PartTarget target, S state) {\n            this.beforeUpdateListeners.forEach(l -> l.onUpdate(network, partNetwork, partType, target, state));\n            super.update(network, partNetwork, partType, target, state);\n            this.afterUpdateListeners.forEach(l -> l.onUpdate(network, partNetwork, partType, target, state));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/build/IAspectUpdateListener.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.build;\n\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * Listens to calls to\n * {@link org.cyclops.integrateddynamics.api.part.aspect.IAspect#update(org.cyclops.integrateddynamics.api.network.INetwork, IPartNetwork, IPartType, PartTarget, IPartState)}.\n * @author rubensworks\n */\npublic interface IAspectUpdateListener {\n\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void onUpdate(INetwork network, IPartNetwork partNetwork, P partType, PartTarget target, S state);\n\n    /**\n     * Before the update is called.\n     */\n    public static interface Before extends IAspectUpdateListener {}\n\n    /**\n     * After the update was called.\n     */\n    public static interface After extends IAspectUpdateListener {}\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/build/IAspectValuePropagator.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.build;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\n\n/**\n * Used for forwarding values to a next propagator.\n * @author rubensworks\n */\npublic interface IAspectValuePropagator<I, O> {\n\n    public O getOutput(I input) throws EvaluationException;\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/build/IAspectWriteActivator.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.build;\n\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\n\n/**\n * Callback for {@link org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite#onActivate(IPartTypeWriter, PartTarget, IPartStateWriter)}.\n * @author rubensworks\n */\npublic interface IAspectWriteActivator {\n\n    /**\n     * When this aspect has become active.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     */\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onActivate(P partType,\n                                                                                            PartTarget target, S state);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/build/IAspectWriteDeactivator.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.build;\n\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\n\n/**\n * Callback for {@link org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite#onDeactivate(IPartTypeWriter, PartTarget, IPartStateWriter)}.\n * @author rubensworks\n */\npublic interface IAspectWriteDeactivator {\n\n    /**\n     * When this aspect has become inactive.\n     * @param partType The part type.\n     * @param target The position that is targeted by the given part.\n     * @param state The current state of the given part.\n     * @param <P> The part type type.\n     * @param <S> The part state.\n     */\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onDeactivate(P partType,\n                                                                                              PartTarget target, S state);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/property/AspectProperties.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.property;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * A property that can be used inside aspects.\n * @author rubensworks\n */\npublic class AspectProperties implements IAspectProperties {\n\n    private final Map<IAspectPropertyTypeInstance, IValue> values = Maps.newLinkedHashMap();\n\n    /**\n     * Make a new instance.\n     * @param propertyTypes The types these properties will have. These will be used to initialize the default values.\n     */\n    public AspectProperties(Collection<IAspectPropertyTypeInstance> propertyTypes) {\n        for(IAspectPropertyTypeInstance propertyType : propertyTypes) {\n            values.put(propertyType, propertyType.getType().getDefault());\n        }\n    }\n\n    /**\n     * Only called for NBT serialization\n     */\n    public AspectProperties() {\n\n    }\n\n    @Override\n    @Deprecated\n    public Collection<IAspectPropertyTypeInstance> getTypes() {\n        return Collections.unmodifiableCollection(values.keySet());\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends IValueType<V>, V extends IValue> V getValue(IAspectPropertyTypeInstance<T, V> type) {\n        IValue value = values.get(type);\n        if (value == null) {\n            value = type.getType().getDefault();\n        }\n        return (V) value;\n    }\n\n    @Override\n    public <T extends IValueType<V>, V extends IValue> void setValue(IAspectPropertyTypeInstance<T, V> type, V value) {\n        values.put(type, value);\n    }\n\n    @Override\n    public <T extends IValueType<V>, V extends IValue> void removeValue(IAspectPropertyTypeInstance<T, V> type) {\n        values.remove(type);\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput) {\n        ValueOutput.ValueOutputList map = valueOutput.childrenList(\"map\");\n        for(Map.Entry<IAspectPropertyTypeInstance, IValue> entry : values.entrySet()) {\n            ValueOutput nbtEntry = map.addChild();\n            nbtEntry.putString(\"key\", entry.getKey().getType().getUniqueName().toString());\n            nbtEntry.putString(\"label\", entry.getKey().getTranslationKey());\n            ValueHelpers.serializeRaw(nbtEntry.child(\"value\"), entry.getValue());\n        }\n    }\n\n    @Override\n    public void deserialize(ValueInput valueInput) {\n        values.clear();\n        for (ValueInput entry : valueInput.childrenList(\"map\").orElseThrow()) {\n            String valueTypeName = entry.getString(\"key\").orElseThrow();\n            IValueType type = ValueTypes.REGISTRY.getValueType(Identifier.parse(valueTypeName));\n            if(type == null) {\n                IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, String.format(\"Could not find value type with name %s, skipping loading.\", valueTypeName));\n            } else {\n                IValue value = ValueHelpers.deserializeRaw(entry.child(\"value\").orElseThrow(), type);\n                String label = entry.getString(\"label\").orElseThrow();\n                if(value == null) {\n                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, String.format(\"The value type %s could not load its value, using default.\", valueTypeName));\n                    value = type.getDefault();\n                }\n                values.put(new AspectPropertyTypeInstance(type, label), value);\n            }\n        }\n    }\n\n    @SuppressWarnings({\"CloneDoesntCallSuperClone\", \"deprecation\"})\n    @Override\n    public IAspectProperties clone() {\n        IAspectProperties clone = new AspectProperties(getTypes());\n        for(IAspectPropertyTypeInstance type : getTypes()) {\n            clone.setValue(type, getValue(type));\n        }\n        return clone;\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/aspect/property/AspectPropertyTypeInstance.java",
    "content": "package org.cyclops.integrateddynamics.core.part.aspect.property;\n\nimport com.google.common.base.Predicates;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\n\nimport java.util.function.Predicate;\n\n/**\n * An instance of a property type with a onLabelPacket.\n * @author rubensworks\n */\npublic class AspectPropertyTypeInstance<T extends IValueType<V>, V extends IValue> implements IAspectPropertyTypeInstance<T, V> {\n\n    private final T type;\n    private final String translationKey;\n    private final Predicate<V> validator;\n\n    public AspectPropertyTypeInstance(T type, String translationKey) {\n        this(type, translationKey, Predicates.<V>alwaysTrue());\n    }\n\n    public AspectPropertyTypeInstance(T type, String translationKey, Predicate<V> validator) {\n        this.type = type;\n        this.translationKey = translationKey;\n        this.validator = validator;\n    }\n\n    public T getType() {\n        return type;\n    }\n\n    public String getTranslationKey() {\n        return translationKey;\n    }\n\n    public Predicate<V> getValidator() {\n        return validator;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        return o instanceof AspectPropertyTypeInstance\n                && ((AspectPropertyTypeInstance<?, ?>) o).type.equals(this.type)\n                && ((AspectPropertyTypeInstance<?, ?>) o).translationKey.equals(this.translationKey);\n    }\n\n    @Override\n    public int hashCode() {\n        return translationKey.hashCode() + type.hashCode() << 2 + 11;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/event/PartAspectEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.part.event;\n\nimport net.minecraft.world.entity.player.Player;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\n\nimport javax.annotation.Nullable;\n\n/**\n * An aspect part event that is posted in the Forge event bus.\n * @author rubensworks\n */\npublic class PartAspectEvent<P extends IPartType<P, S>, S extends IPartState<P>, A extends IAspect> extends PartEvent<P, S> {\n\n    @Nullable\n    private final Player entityPlayer;\n    private final A aspect;\n\n    public PartAspectEvent(INetwork network, IPartNetwork partNetwork, PartTarget target, P partType, S partState, @Nullable Player entityPlayer, A aspect) {\n        super(network, partNetwork, target, partType, partState);\n        this.entityPlayer = entityPlayer;\n        this.aspect = aspect;\n    }\n\n    @Nullable\n    public Player getEntityPlayer() {\n        return entityPlayer;\n    }\n\n    public A getAspect() {\n        return aspect;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/event/PartEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.part.event;\n\nimport net.neoforged.bus.api.Event;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * An part event that is posted in the Forge event bus.\n * @author rubensworks\n */\npublic class PartEvent<P extends IPartType, S extends IPartState<P>> extends Event {\n\n    private final INetwork network;\n    private final IPartNetwork partNetwork;\n    private final PartTarget target;\n    private final P partType;\n    private final S partState;\n\n    public PartEvent(INetwork network, IPartNetwork partNetwork, PartTarget target, P partType, S partState) {\n        this.network = network;\n        this.partNetwork = partNetwork;\n        this.target = target;\n        this.partType = partType;\n        this.partState = partState;\n    }\n\n    public INetwork getNetwork() {\n        return network;\n    }\n\n    public IPartNetwork getPartNetwork() {\n        return partNetwork;\n    }\n\n    public PartTarget getTarget() {\n        return target;\n    }\n\n    public P getPartType() {\n        return partType;\n    }\n\n    public S getPartState() {\n        return partState;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/event/PartReaderAspectEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.part.event;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\n\nimport javax.annotation.Nullable;\n\n/**\n * An event that is posted in the Forge event bus when a reader aspect was written by a player.\n * The resulting itemstack can be modified.\n * @author rubensworks\n */\npublic class PartReaderAspectEvent<P extends IPartTypeReader<P, S>, S extends IPartStateReader<P>, A extends IAspectRead> extends PartAspectEvent<P, S, A> {\n\n    private ItemStack itemStack;\n\n    public PartReaderAspectEvent(INetwork network, IPartNetwork partNetwork, PartTarget target, P partType, S partState,\n                                 @Nullable Player entityPlayer, A aspect, ItemStack itemStack) {\n        super(network, partNetwork, target, partType, partState, entityPlayer, aspect);\n        this.itemStack = itemStack;\n    }\n\n    public ItemStack getItemStack() {\n        return itemStack;\n    }\n\n    public void setItemStack(ItemStack itemStack) {\n        this.itemStack = itemStack;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/event/PartVariableDrivenVariableContentsUpdatedEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.part.event;\n\nimport net.minecraft.world.entity.player.Player;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\nimport javax.annotation.Nullable;\n\n/**\n * An event that is posted in the Forge event bus when the variable contents in a variable-driven part is updated.\n * @author rubensworks\n */\npublic class PartVariableDrivenVariableContentsUpdatedEvent<P extends IPartType<P, S>, S extends IPartState<P>> extends PartEvent<P, S> {\n\n    @Nullable\n    private final Player entityPlayer;\n    @Nullable\n    private final IVariable variable;\n    @Nullable\n    private final IValue value;\n\n    public PartVariableDrivenVariableContentsUpdatedEvent(INetwork network, IPartNetwork partNetwork, PartTarget target, P partType, S partState,\n                                                          @Nullable Player entityPlayer, IVariable variable, IValue value) {\n        super(network, partNetwork, target, partType, partState);\n        this.entityPlayer = entityPlayer;\n        this.variable = variable;\n        this.value = value;\n    }\n\n    @Nullable\n    public Player getEntityPlayer() {\n        return entityPlayer;\n    }\n\n    @Nullable\n    public IValue getValue() {\n        return value;\n    }\n\n    @Nullable\n    public IVariable getVariable() {\n        return variable;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/event/PartWriterAspectEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.part.event;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\n\nimport javax.annotation.Nullable;\n\n/**\n * An event that is posted in the Forge event bus when a write aspect is enabled by a player.\n * @author rubensworks\n */\npublic class PartWriterAspectEvent<P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>, A extends IAspectWrite> extends PartAspectEvent<P, S, A> {\n\n    private final ItemStack itemStack;\n\n    public PartWriterAspectEvent(INetwork network, IPartNetwork partNetwork, PartTarget target, P partType, S partState,\n                                 @Nullable Player entityPlayer, A aspect, ItemStack itemStack) {\n        super(network, partNetwork, target, partType, partState, entityPlayer, aspect);\n        this.itemStack = itemStack;\n    }\n\n    public ItemStack getItemStack() {\n        return itemStack;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/event/RegisterPartCapabilitiesEvent.java",
    "content": "package org.cyclops.integrateddynamics.core.part.event;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.neoforged.bus.api.Event;\nimport net.neoforged.fml.event.IModBusEvent;\nimport net.neoforged.neoforge.capabilities.BlockCapability;\nimport net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartCapability;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\n\n/**\n * An event to link block capabilities to part capabilities.\n * This assumes part can expose volatile capabilities.\n * @author rubensworks\n */\npublic class RegisterPartCapabilitiesEvent extends Event implements IModBusEvent {\n\n    protected final RegisterCapabilitiesEvent registerCapabilitiesEvent;\n    protected final BlockEntityType<? extends BlockEntityMultipartTicking> blockEntityType;\n\n    public RegisterPartCapabilitiesEvent(\n            RegisterCapabilitiesEvent registerCapabilitiesEvent,\n            BlockEntityType<? extends BlockEntityMultipartTicking> blockEntityType\n    ) {\n        this.registerCapabilitiesEvent = registerCapabilitiesEvent;\n        this.blockEntityType = blockEntityType;\n    }\n\n    public <T> void register(BlockCapability<T, Direction> blockCapability, PartCapability<T> partCapability) {\n        this.registerCapabilitiesEvent.registerBlockEntity(\n                blockCapability,\n                blockEntityType,\n                (blockEntity, context) -> {\n                    INetwork network = blockEntity.getNetwork();\n                    if (network != null) {\n                        IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n                        return blockEntity.getPartContainer()\n                                .getCapability(partCapability, network, partNetwork, PartTarget.fromCenter(PartPos.of(blockEntity.getLevel(), blockEntity.getBlockPos(), context)))\n                                .orElse(null);\n                    }\n                    return null;\n                }\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/panel/PartTypePanel.java",
    "content": "package org.cyclops.integrateddynamics.core.part.panel;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\n\n/**\n * A base part that is flat and can be used to render things on.\n * @author rubensworks\n */\npublic abstract class PartTypePanel<P extends PartTypePanel<P, S>, S extends IPartState<P>> extends PartTypeBase<P, S> {\n\n    public PartTypePanel(String name) {\n        super(name, new PartRenderPosition(0.125F, 0.1875F, 0.625F, 0.625F));\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlockStatus(properties);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/panel/PartTypePanelVariableDriven.java",
    "content": "package org.cyclops.integrateddynamics.core.part.panel;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\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.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.IPartTypeActiveVariable;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlock;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.core.part.PartStateActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerPartPanelVariableDriven;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * A panel part that is driven by a contained variable.\n * @author rubensworks\n */\npublic abstract class PartTypePanelVariableDriven<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>> extends PartTypePanel<P, S> implements IPartTypeActiveVariable<P, S> {\n\n    public PartTypePanelVariableDriven(String name) {\n        super(name);\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlockStatus(properties);\n    }\n\n    @Override\n    protected Map<Class<? extends INetworkEvent>, IEventAction> constructNetworkEventActions() {\n        Map<Class<? extends INetworkEvent>, IEventAction> actions = super.constructNetworkEventActions();\n        IEventAction<P, S, INetworkEvent> updateEventListener = (network, target, state, event) -> NetworkHelpers\n                .getPartNetwork(network).ifPresent(partNetwork -> onVariableContentsUpdated(partNetwork, target, state));\n        actions.put(VariableContentsUpdatedEvent.class, updateEventListener);\n        return actions;\n    }\n\n    @Override\n    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n        for(int i = 0; i < state.getInventory().getContainerSize(); i++) {\n            ItemStack itemStack = state.getInventory().getItem(i);\n            if(!itemStack.isEmpty()) {\n                itemStacks.add(itemStack);\n            }\n        }\n        state.getInventory().clearContent();\n        state.onVariableContentsUpdated((P) this, target);\n        super.addDrops(target, state, itemStacks, dropMainElement, saveState);\n    }\n\n    @Override\n    public boolean isUpdate(S state) {\n        return true;\n    }\n\n    @Override\n    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        super.update(network, partNetwork, target, state);\n        IValue lastValue = state.getDisplayValue();\n        IValue newValue = null;\n        Level level = target.getCenter().getPos().getLevel(true);\n        if(state.hasVariable()) {\n            try {\n                IVariable variable = state.getVariable(network, partNetwork, ValueDeseralizationContext.of(level));\n                if(variable != null) {\n                    newValue = variable.getValue();\n\n                    if (state.isRetryEvaluation()) {\n                        state.setRetryEvaluation(false);\n                        state.addGlobalError(null);\n                    }\n                }\n            } catch (EvaluationException e) {\n                if (!state.isRetryEvaluation()) {\n                    state.addGlobalError(e.getErrorMessage());\n                    if (e.isRetryEvaluation()) {\n                        state.setRetryEvaluation(true);\n                    } else {\n                        e.addResolutionListeners(() -> state.onVariableContentsUpdated((P) this, target));\n                    }\n                }\n            }\n        }\n        if(!ValueHelpers.areValuesEqual(lastValue, newValue)) {\n            onValueChanged(network, partNetwork, target, state, lastValue, newValue);\n\n            // We can't call state.sendUpdate() here, so we must trigger a block update manually.\n            // This was the cause of issue #46 which made it so that values that change after one tick are\n            // NOT sent to the client.\n            // This was because in each server tick, all tiles are first updated and then the networks.\n            // Since sendUpdate marks a flag for the part to update, this caused a loss of one tick.\n            // For example:\n            // tick-0: Tile tick\n            // tick-0: Part tick: update a value, and mark part to invalidate\n            // tick-0: -- send all block updates to client ---\n            // tick-1: Tile tick: notices and update, marks a block invalidate\n            // tick-1: Part tick: update the value again, the old value has still not been sent here!\n            // tick-1: -- send all block updates to client --- This will contain the value that was set in tick-1.\n            state.onDirty();\n            IModHelpers.get().getBlockHelpers().markForUpdate(level, target.getCenter().getPos().getBlockPos());\n        }\n    }\n\n    @Override\n    public boolean hasActiveVariable(IPartNetwork network, PartTarget target, S partState) {\n        return partState.hasVariable();\n    }\n\n    @Override\n    public <V extends IValue> IVariable<V> getActiveVariable(INetwork network, IPartNetwork partNetwork, PartTarget target, S partState) {\n        return partState.getVariable(network, partNetwork, ValueDeseralizationContext.of(target.getCenter().getPos().getLevel(true)));\n    }\n\n    protected void onValueChanged(INetwork network, IPartNetwork partNetwork, PartTarget target, S state,\n                                  IValue lastValue, IValue newValue) {\n        if (newValue == null) {\n            state.setDisplayValue(null);\n        } else {\n            IValue materializedValue = null;\n            try {\n                if (newValue.getType() == ValueTypes.LIST) {\n                    IValueTypeListProxy<IValueType<IValue>, IValue> original = ((ValueTypeList.ValueList) newValue).getRawValue();\n                    if (original.getLength() > ValueTypeList.MAX_RENDER_LINES) {\n                        List<IValue> list = Lists.newArrayList();\n                        for (int i = 0; i < ValueTypeList.MAX_RENDER_LINES; i++) {\n                            list.add(original.get(i));\n                        }\n                        newValue = ValueTypeList.ValueList.ofList(original.getValueType(), list);\n                    }\n                }\n                materializedValue = newValue.getType().materialize(newValue);\n            } catch (EvaluationException e) {\n                state.addGlobalError(e.getErrorMessage());\n                e.addResolutionListeners(() -> state.addGlobalError(null));\n            }\n            state.setDisplayValue(materializedValue);\n        }\n    }\n\n    @Override\n    public Optional<MenuProvider> getContainerProvider(PartPos pos) {\n        return Optional.of(new MenuProvider() {\n            @Override\n            public Component getDisplayName() {\n                return Component.translatable(getTranslationKey());\n            }\n\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);\n                PartTypePanelVariableDriven.State partState = (PartTypePanelVariableDriven.State) data.getLeft().getPartState(data.getRight().getCenter().getSide());\n                return new ContainerPartPanelVariableDriven(id, playerInventory, partState.getInventory(),\n                        Optional.of(data.getRight()), Optional.of(data.getLeft()), (PartTypePanelVariableDriven<?, ?>) data.getMiddle());\n            }\n\n            @Override\n            public boolean shouldTriggerClientSideContainerClosingOnOpen() {\n                return false;\n            }\n        });\n    }\n\n    @Override\n    public void writeExtraGuiData(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n        // Write inventory size\n        IPartContainer partContainer = PartHelpers.getPartContainerChecked(pos);\n        PartTypePanelVariableDriven.State partState = (PartTypePanelVariableDriven.State) partContainer.getPartState(pos.getSide());\n        packetBuffer.writeInt(partState.getInventory().getContainerSize());\n\n        super.writeExtraGuiData(packetBuffer, pos, player);\n    }\n\n    protected IgnoredBlockStatus.Status getStatus(PartTypePanelVariableDriven.State state) {\n        IgnoredBlockStatus.Status status = IgnoredBlockStatus.Status.INACTIVE;\n        if (state != null && !state.getInventory().isEmpty()) {\n            if (state.hasVariable() && state.isEnabled()) {\n                status = IgnoredBlockStatus.Status.ACTIVE;\n            } else {\n                status = IgnoredBlockStatus.Status.ERROR;\n            }\n        }\n        return status;\n    }\n\n    @Override\n    public BlockState getBlockState(IPartContainer partContainer,\n                                    Direction side) {\n        IgnoredBlockStatus.Status status = getStatus(partContainer != null\n                ? (PartTypePanelVariableDriven.State) partContainer.getPartState(side) : null);\n        return getBlock().defaultBlockState()\n                .setValue(IgnoredBlock.FACING, side)\n                .setValue(IgnoredBlockStatus.STATUS, status);\n    }\n\n    protected void onVariableContentsUpdated(IPartNetwork network, PartTarget target, S state) {\n        state.onVariableContentsUpdated((P) this, target);\n    }\n\n    @Override\n    public InteractionResult onPartActivated(final S partState, BlockPos pos, Level world, Player player, InteractionHand hand,\n                                             ItemStack heldItem, BlockHitResult hit) {\n        if(WrenchHelpers.isWrench(player, heldItem, world, pos, hit.getDirection())) {\n            WrenchHelpers.wrench(player, heldItem, world, pos, hit.getDirection(),\n                    (player1, pos1, parameter) -> partState.setFacingRotation(partState.getFacingRotation().getClockWise()));\n            return InteractionResult.SUCCESS;\n        }\n        return super.onPartActivated(partState, pos, world, player, hand, heldItem, hit);\n    }\n\n    @Override\n    public void loadTooltip(S state, List<Component> lines) {\n        if (!state.getInventory().isEmpty()) {\n            if (state.hasVariable() && state.isEnabled()) {\n                IValue value = state.getDisplayValue();\n                if(value != null) {\n                    IValueType valueType = value.getType();\n                    lines.add(Component.translatable(\n                            L10NValues.PART_TOOLTIP_DISPLAY_ACTIVEVALUE,\n                            valueType.toCompactString(value).withStyle(valueType.getDisplayColorFormat()),\n                            Component.translatable(valueType.getTranslationKey())));\n                }\n            } else {\n                lines.add(Component.translatable(L10NValues.PART_TOOLTIP_ERRORS).withStyle(ChatFormatting.RED));\n                for (Component error : state.getGlobalErrors()) {\n                    lines.add(error.copy().withStyle(ChatFormatting.RED));\n                }\n            }\n        } else {\n            lines.add(Component.translatable(L10NValues.PART_TOOLTIP_INACTIVE));\n        }\n        super.loadTooltip(state, lines);\n    }\n\n    @Override\n    public boolean shouldTriggerBlockRenderUpdate(@Nullable S oldPartState, @Nullable S newPartState) {\n        return super.shouldTriggerBlockRenderUpdate(oldPartState, newPartState)\n                || getStatus(oldPartState) != getStatus(newPartState);\n    }\n\n    public static abstract class State<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>> extends PartStateActiveVariableBase<P> {\n\n        private IValue displayValue;\n        private Direction facingRotation = Direction.NORTH;\n\n        public IValue getDisplayValue() {\n            return displayValue;\n        }\n\n        public void setDisplayValue(IValue displayValue) {\n            this.displayValue = displayValue;\n        }\n\n        public Direction getFacingRotation() {\n            return facingRotation;\n        }\n\n        public void setFacingRotation(Direction facingRotation) {\n            this.facingRotation = facingRotation;\n        }\n\n        public State() {\n            super(1);\n        }\n\n        @Override\n        public void serialize(ValueOutput valueOutput) {\n            super.serialize(valueOutput);\n            IValue value = getDisplayValue();\n            if(value != null) {\n                valueOutput.putString(\"displayValueType\", value.getType().getUniqueName().toString());\n                ValueHelpers.serializeRaw(valueOutput.child(\"displayValue\"), value);\n            }\n            valueOutput.putInt(\"facingRotation\", facingRotation.ordinal());\n        }\n\n        @Override\n        public void deserialize(ValueInput valueInput) {\n            super.deserialize(valueInput);\n            valueInput.getString(\"displayValueType\")\n                    .ifPresentOrElse(displayValueType -> {\n                        IValueType valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(displayValueType));\n                        if(valueType != null) {\n                            ValueInput serializedValue = valueInput.child(\"displayValue\").orElseThrow();\n                            Component deserializationError = valueType.canDeserialize(serializedValue);\n                            if(deserializationError == null) {\n                                setDisplayValue(ValueHelpers.deserializeRaw(serializedValue, valueType));\n                            } else {\n                                IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, deserializationError.getString());\n                            }\n                        } else {\n                            IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR,\n                                    String.format(\"Tried to deserialize the value \\\"%s\\\" for type \\\"%s\\\" which could not be found.\",\n                                            valueInput.getString(\"displayValueType\"), valueInput.getString(\"value\")));\n                        }\n                    }, () -> {\n                        setDisplayValue(null);\n                    });\n            facingRotation = Direction.values()[Math.max(2, valueInput.getInt(\"facingRotation\").orElseThrow())];\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/read/PartStateReaderBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part.read;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.core.part.PartStateBase;\n\nimport java.util.IdentityHashMap;\nimport java.util.Map;\n\n/**\n * A default implementation of the {@link IPartStateReader}.\n * @author rubensworks\n */\npublic class PartStateReaderBase<P extends IPartTypeReader>\n        extends PartStateBase<P> implements IPartStateReader<P> {\n\n    private final Map<IAspect, IAspectVariable> aspectVariables = new IdentityHashMap<>();\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <V extends IValue, T extends IValueType<V>> IAspectVariable<V> getVariable(IAspectRead<V, T> aspect) {\n        return aspectVariables.get(aspect);\n    }\n\n    @Override\n    public void setVariable(IAspect aspect, IAspectVariable variable) {\n        aspectVariables.put(aspect, variable);\n    }\n\n    @Override\n    public void resetVariables() {\n        for (IAspectVariable variable : this.aspectVariables.values()) {\n            variable.invalidate();\n        }\n    }\n\n    @Override\n    public void setAspectProperties(IAspect aspect, IAspectProperties properties) {\n        super.setAspectProperties(aspect, properties);\n        this.aspectVariables.remove(aspect);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/read/PartTypeReadBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part.read;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.level.BlockGetter;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.AspectUpdateType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypeAspects;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerPartReader;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport javax.annotation.Nullable;\nimport java.util.EnumMap;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\n/**\n * An abstract {@link IPartTypeReader}.\n * @author rubensworks\n */\npublic abstract class PartTypeReadBase<P extends IPartTypeReader<P, S>, S extends IPartStateReader<P>>\n        extends PartTypeAspects<P, S> implements IPartTypeReader<P, S> {\n\n    private List<IAspectRead<?, ?>> aspectsRead = null;\n    private EnumMap<AspectUpdateType, Set<IAspectRead<?, ?>>> updateAspects = null;\n\n    public PartTypeReadBase(String name) {\n        this(name, new PartRenderPosition(0.1875F, 0.3125F, 0.625F, 0.625F));\n    }\n\n    public PartTypeReadBase(String name, PartRenderPosition partRenderPosition) {\n        super(name, partRenderPosition);\n    }\n\n    protected Set<IAspectRead<?, ?>> getUpdateAspects(AspectUpdateType updateType) {\n        if (updateAspects == null) {\n            updateAspects = new EnumMap<>(AspectUpdateType.class);\n            for (AspectUpdateType aspectUpdateType : AspectUpdateType.values()) {\n                updateAspects.put(aspectUpdateType, Sets.newLinkedHashSet());\n            }\n            for (IAspect aspect : getAspects()) {\n                if (aspect instanceof IAspectRead) {\n                    IAspectRead aspectRead = (IAspectRead) aspect;\n                    updateAspects.get(aspectRead.getUpdateType()).add(aspectRead);\n                }\n            }\n        }\n\n        return updateAspects.get(updateType);\n    }\n\n    @Override\n    public boolean isSolid(S state) {\n        return true;\n    }\n\n    @Override\n    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        for(IAspect aspect : getUpdateAspects(AspectUpdateType.NETWORK_TICK)) {\n            aspect.update(network, partNetwork, this, target, state);\n        }\n\n        // Special case: if we have an offset, also update block-update-based aspects, because we can't rely on just block updates.\n        if (this.getTargetOffset(state).compareTo(Vec3i.ZERO) != 0) {\n            for(IAspect aspect : getUpdateAspects(AspectUpdateType.BLOCK_UPDATE)) {\n                aspect.update(network, partNetwork, this, target, state);\n            }\n        }\n\n        // We call super as last because super will invoke the offset handler, which can cause the PartTarget to change.\n        // This PartTarget change can conflict with aspects, and cause weird delay problems, such as #1320,\n        // So that's why we delay it so it only applies for the next tick.\n        super.update(network, partNetwork, target, state);\n    }\n\n    @Override\n    public void onBlockNeighborChange(INetwork network, IPartNetwork partNetwork, PartTarget target, S state,\n                                      BlockGetter world, @Nullable Direction side) {\n        super.onBlockNeighborChange(network, partNetwork, target, state, world, side);\n        for(IAspect aspect : getUpdateAspects(AspectUpdateType.BLOCK_UPDATE)) {\n            aspect.update(network, partNetwork, this, target, state);\n        }\n    }\n\n    @Override\n    public List<IAspectRead<?, ?>> getReadAspects() {\n        if (aspectsRead == null) {\n            aspectsRead = Aspects.REGISTRY.getReadAspects(this);\n        }\n        return aspectsRead;\n    }\n\n    @Override\n    public <V extends IValue, T extends IValueType<V>> IAspectVariable<V> getVariable(PartTarget target, S partState,\n                                                                                      IAspectRead<V, T> aspect) {\n        IAspectVariable<V> variable = partState.getVariable(aspect);\n        if(variable == null) {\n            if(!getAspects().contains(aspect)) {\n                throw new IllegalArgumentException(String.format(\"Tried to get the variable for the aspect %s that did not exist within the \" +\n                        \"part type %s.\", aspect.getUniqueName(), this));\n            }\n            variable = aspect.createNewVariable(() -> getTarget(target.getCenter(), partState));\n            partState.setVariable(aspect, variable);\n        }\n        return variable;\n    }\n\n    @Override\n    public boolean setTargetOffset(S state, PartPos center, Vec3i offset) {\n        Vec3i lastOffset = getTargetOffset(state);\n        boolean ret = super.setTargetOffset(state, center, offset);\n        if (!lastOffset.equals(offset)) {\n            state.resetVariables();\n        }\n        return ret;\n    }\n\n    @Override\n    public void setTargetSideOverride(S state, @Nullable Direction side) {\n        Direction lastSide = getTargetSideOverride(state);\n        super.setTargetSideOverride(state, side);\n        if (lastSide != side) {\n            state.resetVariables();\n        }\n    }\n\n    @Override\n    public Optional<MenuProvider> getContainerProvider(PartPos pos) {\n        return Optional.of(new MenuProvider() {\n            @Override\n            public Component getDisplayName() {\n                return Component.translatable(getTranslationKey());\n            }\n\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);\n                return new ContainerPartReader<>(id, playerInventory, new SimpleContainer(0),\n                        data.getRight(), Optional.of(data.getLeft()), (PartTypeReadBase) data.getMiddle());\n            }\n\n            @Override\n            public boolean shouldTriggerClientSideContainerClosingOnOpen() {\n                return false;\n            }\n        });\n    }\n\n    @Override\n    public void writeExtraGuiData(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n        // Write part position\n        PacketCodecs.write(packetBuffer, pos);\n\n        super.writeExtraGuiData(packetBuffer, pos, player);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/write/PartStateWriterBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part.write;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.helper.CollectionHelpers;\nimport org.cyclops.cyclopscore.persist.nbt.NBTClassType;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.part.PartStateActiveVariableBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * A default implementation of the {@link IPartTypeWriter}.\n * @author rubensworks\n */\npublic class PartStateWriterBase<P extends IPartTypeWriter>\n        extends PartStateActiveVariableBase<P> implements IPartStateWriter<P> {\n\n    private IAspectWrite activeAspect = null;\n    private Map<String, List<Component>> errorMessages = Maps.newHashMap();\n    private boolean firstTick = true;\n\n    public PartStateWriterBase(int inventorySize) {\n        super(inventorySize);\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput) {\n        if (this.activeAspect != null) valueOutput.putString(\"activeAspectName\", this.activeAspect.getUniqueName().toString());\n        NBTClassType.getType(Map.class, this.errorMessages).writePersistedField(\"errorMessages\", this.errorMessages, valueOutput);\n        super.serialize(valueOutput);\n    }\n\n    @Override\n    public void deserialize(ValueInput valueInput) {\n        valueInput.getString(\"activeAspectName\").ifPresent(activeAspect -> {\n            IAspect aspect = Aspects.REGISTRY.getAspect(Identifier.parse(activeAspect));\n            if (aspect instanceof IAspectWrite) {\n                this.activeAspect = (IAspectWrite) aspect;\n            }\n        });\n        this.errorMessages = (Map<String, List<Component>>) NBTClassType.getType(Map.class, this.errorMessages).readPersistedField(\"errorMessages\", valueInput);\n        super.deserialize(valueInput);\n    }\n\n    @Override\n    protected void validate(INetwork network, IPartNetwork partNetwork) {\n        // Note that this is only called server-side, so these errors are sent via NBT to the client(s).\n        if(getActiveAspect() != null) {\n            this.currentVariableFacade.validate(network, partNetwork,\n                    new PartStateWriterBase.Validator(this, getActiveAspect()), getActiveAspect().getValueType());\n        }\n    }\n\n    @Override\n    protected void onCorruptedState() {\n        super.onCorruptedState();\n        this.activeAspect = null;\n    }\n\n    @Override\n    public boolean hasVariable() {\n        return getActiveAspect() != null && getErrors(getActiveAspect()).isEmpty() && super.hasVariable();\n    }\n\n    @Override\n    public void triggerAspectInfoUpdate(P partType, PartTarget target, IAspectWrite newAspect, boolean isNetworkInitializing) {\n        if (!isNetworkInitializing) {\n            // We skip network content updates during network init,\n            // as it will be called once for all parts right after network init.\n            // This is to avoid re-updating variable contents many times during network init, which can get expensive.\n            onVariableContentsUpdated(partType, target);\n\n            IAspectWrite activeAspect = getActiveAspect();\n            if(activeAspect != null && activeAspect != newAspect) {\n                activeAspect.onDeactivate(partType, target, this);\n            }\n            if(newAspect != null && activeAspect != newAspect) {\n                newAspect.onActivate(partType, target, this);\n            }\n            this.activeAspect = newAspect;\n        }\n    }\n\n    @Override\n    public void onVariableContentsUpdated(P partType, PartTarget target) {\n        // Resets the errors for this aspect\n        super.onVariableContentsUpdated(partType, target);\n        IAspectWrite activeAspect = getActiveAspect();\n        if(activeAspect != null) {\n            addError(activeAspect, null);\n        }\n    }\n\n    @Override\n    public IAspectWrite getActiveAspect() {\n        return activeAspect;\n    }\n\n    @Override\n    public List<Component> getErrors(IAspectWrite aspect) {\n        List<Component> errors = errorMessages.get(aspect.getUniqueName().toString());\n        if(errors == null) {\n            return Collections.emptyList();\n        }\n        return errors;\n    }\n\n    @Override\n    public void addError(IAspectWrite aspect, MutableComponent error) {\n        if(error == null) {\n            errorMessages.remove(aspect.getUniqueName().toString());\n        } else {\n            CollectionHelpers.addToMapList(errorMessages, aspect.getUniqueName().toString(), error);\n        }\n        onDirty();\n        sendUpdate(); // We want this error messages to be sent to the client(s).\n    }\n\n    @Override\n    public boolean checkAndResetFirstTick() {\n        if(firstTick) {\n            firstTick = false;\n            return true;\n        }\n        return false;\n    }\n\n    public static class Validator implements IVariableFacade.IValidator {\n\n        private final IPartStateWriter state;\n        private final IAspectWrite aspect;\n\n        /**\n         * Make a new instance\n         * @param state The part state.\n         * @param aspect The aspect to set the error for.\n         */\n        public Validator(IPartStateWriter state, IAspectWrite aspect) {\n            this.state = state;\n            this.aspect = aspect;\n        }\n\n        @Override\n        public void addError(MutableComponent error) {\n            this.state.addError(aspect, error);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/part/write/PartTypeWriteBase.java",
    "content": "package org.cyclops.integrateddynamics.core.part.write;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\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.ItemStack;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.network.event.INetworkEvent;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlock;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.core.part.PartTypeAspects;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\nimport org.cyclops.integrateddynamics.core.part.event.PartWriterAspectEvent;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerPartWriter;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * An abstract {@link IPartTypeWriter}.\n * @author rubensworks\n */\npublic abstract class PartTypeWriteBase<P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>>\n        extends PartTypeAspects<P, S> implements IPartTypeWriter<P, S> {\n\n    private List<IAspectWrite<?, ?>> aspectsWrite = null;\n\n    public PartTypeWriteBase(String name) {\n        this(name, new PartRenderPosition(0.3125F, 0.3125F, 0.625F, 0.625F, 0.25F, 0.25F));\n    }\n\n    public PartTypeWriteBase(String name, PartRenderPosition partRenderPosition) {\n        super(name, partRenderPosition);\n    }\n\n    @Override\n    protected Map<Class<? extends INetworkEvent>, IEventAction> constructNetworkEventActions() {\n        Map<Class<? extends INetworkEvent>, IEventAction> actions = super.constructNetworkEventActions();\n        IEventAction<P, S, INetworkEvent> updateEventListener = (network, target, state, event) -> NetworkHelpers\n                .getPartNetwork(network).ifPresent(partNetwork -> onVariableContentsUpdated(partNetwork, target, state));\n        actions.put(VariableContentsUpdatedEvent.class, updateEventListener);\n        return actions;\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlockStatus(properties);\n    }\n\n    @Override\n    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        super.update(network, partNetwork, target, state);\n        IAspect aspect = getActiveAspect(target, state);\n        if (aspect != null) {\n            aspect.update(network, partNetwork, this, target, state);\n        }\n    }\n\n    @Override\n    public boolean setTargetOffset(S state, PartPos center, Vec3i offset) {\n        IAspectWrite activeAspect = state.getActiveAspect();\n        if(activeAspect != null) {\n            activeAspect.onDeactivate(this, getTarget(center, state), state);\n        }\n        boolean ret = super.setTargetOffset(state, center, offset);\n        if(activeAspect != null) {\n            activeAspect.onActivate(this, getTarget(center, state), state);\n        }\n        return ret;\n    }\n\n    @Override\n    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n        for(int i = 0; i < state.getInventory().getContainerSize(); i++) {\n            ItemStack itemStack = state.getInventory().getItem(i);\n            if(!itemStack.isEmpty()) {\n                itemStacks.add(itemStack);\n            }\n        }\n        state.getInventory().clearContent();\n        state.triggerAspectInfoUpdate((P) this, target, null, false);\n        super.addDrops(target, state, itemStacks, dropMainElement, saveState);\n    }\n\n    @Override\n    public void beforeNetworkKill(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        super.beforeNetworkKill(network, partNetwork, target, state);\n        state.triggerAspectInfoUpdate((P) this, target, null, true);\n    }\n\n    @Override\n    public void afterNetworkAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        super.afterNetworkAlive(network, partNetwork, target, state);\n        updateActivation(target, state, null, true);\n    }\n\n    @Override\n    public List<IAspectWrite<?, ?>> getWriteAspects() {\n        if (aspectsWrite == null) {\n            aspectsWrite = Aspects.REGISTRY.getWriteAspects(this);\n        }\n        return aspectsWrite;\n    }\n\n    @Override\n    public boolean hasActiveVariable(IPartNetwork network, PartTarget target, S partState) {\n        return partState.hasVariable();\n    }\n\n    @Override\n    public <V extends IValue> IVariable<V> getActiveVariable(INetwork network, IPartNetwork partNetwork, PartTarget target, S partState) {\n        return partState.getVariable(network, partNetwork, ValueDeseralizationContext.of(target.getCenter().getPos().getLevel(true)));\n    }\n\n    @Override\n    public IAspectWrite getActiveAspect(PartTarget target, S partState) {\n        return partState.getActiveAspect();\n    }\n\n    @Override\n    public void updateActivation(PartTarget target, S partState, @Nullable Player player, boolean isNetworkInitializing) {\n        // Check inside the inventory for a variable item and determine everything with that.\n        int activeIndex = -1;\n        for(int i = 0 ; i < partState.getInventory().getContainerSize(); i++) {\n            if(!partState.getInventory().getItem(i).isEmpty()) {\n                activeIndex = i;\n                break;\n            }\n        }\n        IAspectWrite aspect = activeIndex == -1 ? null : getWriteAspects().get(activeIndex);\n        partState.triggerAspectInfoUpdate((P) this, target, aspect, isNetworkInitializing);\n\n        INetwork network = NetworkHelpers.getNetwork(target.getCenter()).orElse(null);\n        if (network != null && aspect != null) {\n            IPartNetwork partNetwork = NetworkHelpers.getPartNetwork(network).orElse(null);\n            if (partNetwork != null) {\n                NeoForge.EVENT_BUS.post(new PartWriterAspectEvent<>(network, partNetwork, target, (P) this,\n                        partState, player, aspect, partState.getInventory().getItem(activeIndex)));\n            }\n        }\n        if (network != null && !isNetworkInitializing) {\n            network.getEventBus().post(new VariableContentsUpdatedEvent(network));\n        }\n    }\n\n    protected void onVariableContentsUpdated(IPartNetwork network, PartTarget target, S state) {\n        state.onVariableContentsUpdated((P) this, target);\n    }\n\n    protected IgnoredBlockStatus.Status getStatus(IPartStateWriter state) {\n        IgnoredBlockStatus.Status status = IgnoredBlockStatus.Status.INACTIVE;\n        if (state != null && !state.getInventory().isEmpty()) {\n            if (state.hasVariable() && state.isEnabled()) {\n                status = IgnoredBlockStatus.Status.ACTIVE;\n            } else {\n                status = IgnoredBlockStatus.Status.ERROR;\n            }\n        }\n        return status;\n    }\n\n    @Override\n    public BlockState getBlockState(IPartContainer partContainer,\n                                    Direction side) {\n        IgnoredBlockStatus.Status status = getStatus(partContainer != null\n                ? (IPartStateWriter) partContainer.getPartState(side) : null);\n        return getBlock().defaultBlockState().setValue(IgnoredBlock.FACING, side)\n                .setValue(IgnoredBlockStatus.STATUS, status);\n    }\n\n    @Override\n    public void loadTooltip(S state, List<Component> lines) {\n        super.loadTooltip(state, lines);\n        IAspectWrite aspectWrite = state.getActiveAspect();\n        if (aspectWrite != null) {\n            if (state.hasVariable() && state.isEnabled()) {\n                lines.add(Component.translatable(\n                        L10NValues.PART_TOOLTIP_WRITER_ACTIVEASPECT,\n                        Component.translatable(aspectWrite.getTranslationKey()),\n                        Component.translatable(aspectWrite.getValueType().getTranslationKey())\n                                .withStyle(aspectWrite.getValueType().getDisplayColorFormat())));\n            } else {\n                lines.add(Component.translatable(L10NValues.PART_TOOLTIP_ERRORS)\n                        .withStyle(ChatFormatting.RED));\n                for (Component error : state.getErrors(aspectWrite)) {\n                    lines.add(error.copy().withStyle(ChatFormatting.RED));\n                }\n            }\n        } else {\n            lines.add(Component.translatable(L10NValues.PART_TOOLTIP_INACTIVE));\n        }\n    }\n\n    @Override\n    public Optional<MenuProvider> getContainerProvider(PartPos pos) {\n        return Optional.of(new MenuProvider() {\n            @Override\n            public Component getDisplayName() {\n                return Component.translatable(getTranslationKey());\n            }\n\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);\n                S partState = (S) data.getLeft().getPartState(data.getRight().getCenter().getSide());\n                return new ContainerPartWriter<>(id, playerInventory, partState.getInventory(),\n                        data.getRight(), Optional.of(data.getLeft()), (PartTypeWriteBase) data.getMiddle());\n            }\n\n            @Override\n            public boolean shouldTriggerClientSideContainerClosingOnOpen() {\n                return false;\n            }\n        });\n    }\n\n    @Override\n    public void writeExtraGuiData(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {\n        // Write inventory size\n        packetBuffer.writeInt(getWriteAspects().size());\n        // Write part position\n        PacketCodecs.write(packetBuffer, pos);\n\n        super.writeExtraGuiData(packetBuffer, pos, player);\n    }\n\n    @Override\n    public boolean shouldTriggerBlockRenderUpdate(@Nullable S oldPartState, @Nullable S newPartState) {\n        return super.shouldTriggerBlockRenderUpdate(oldPartState, newPartState)\n                || getStatus(oldPartState) != getStatus(newPartState);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/path/Cluster.java",
    "content": "package org.cyclops.integrateddynamics.core.path;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\nimport org.cyclops.integrateddynamics.api.path.SidedPathElementParams;\nimport org.cyclops.integrateddynamics.capability.path.SidedPathElement;\n\nimport java.util.*;\n\n/**\n * A cluster for a collection of path elements.\n * @author rubensworks\n */\npublic class Cluster implements Collection<ISidedPathElement> {\n\n    private final Set<ISidedPathElement> elements;\n\n    /**\n     * This constructor should not be called, except for the process of constructing networks from NBT.\n     */\n    public Cluster() {\n        this.elements = Sets.newTreeSet();\n    }\n\n    public Cluster(TreeSet<ISidedPathElement> elements) {\n        this.elements = elements;\n    }\n\n    public Set<ISidedPathElement> getElements() {\n        return elements;\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        Cluster that = (Cluster) o;\n        return Objects.equals(elements, that.elements);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(elements);\n    }\n\n    @Override\n    public String toString() {\n        return \"Cluster{\" +\n                \"elements=\" + elements +\n                '}';\n    }\n\n    // Delegate methods to elements Set\n    @Override\n    public int size() {\n        return elements.size();\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return elements.isEmpty();\n    }\n\n    @Override\n    public boolean contains(Object o) {\n        return elements.contains(o);\n    }\n\n    @Override\n    public Iterator<ISidedPathElement> iterator() {\n        return elements.iterator();\n    }\n\n    @Override\n    public Object[] toArray() {\n        return elements.toArray();\n    }\n\n    @Override\n    public <T> T[] toArray(T[] a) {\n        return elements.toArray(a);\n    }\n\n    @Override\n    public boolean add(ISidedPathElement iSidedPathElement) {\n        return elements.add(iSidedPathElement);\n    }\n\n    @Override\n    public boolean remove(Object o) {\n        return elements.remove(o);\n    }\n\n    @Override\n    public boolean containsAll(Collection<?> c) {\n        return elements.containsAll(c);\n    }\n\n    @Override\n    public boolean addAll(Collection<? extends ISidedPathElement> c) {\n        return elements.addAll(c);\n    }\n\n    @Override\n    public boolean removeAll(Collection<?> c) {\n        return elements.removeAll(c);\n    }\n\n    @Override\n    public boolean retainAll(Collection<?> c) {\n        return elements.retainAll(c);\n    }\n\n    @Override\n    public void clear() {\n        elements.clear();\n    }\n\n    public void fromParams(List<SidedPathElementParams> pathElements) {\n        for (SidedPathElementParams pathElementParam : pathElements) {\n            Identifier dimensionId = Identifier.parse(pathElementParam.dimension());\n            ResourceKey<Level> dimension = ResourceKey.create(Registries.DIMENSION, dimensionId);\n            Level world = ServerLifecycleHooks.getCurrentServer().getLevel(dimension);\n            BlockPos pos = pathElementParam.pos();\n            Direction side = pathElementParam.side().orElse(null);\n\n            if (world == null) {\n                IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"Skipped loading part from a network at the \" +\n                        \"invalid dimension id %s.\", dimensionId));\n            } else {\n                IPathElement pathElement = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(world, pos, side, Capabilities.PathElement.BLOCK).orElse(null);\n                if(pathElement == null) {\n                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(\"Skipped loading part from a network at \" +\n                            \"position %s in world %s because it has no valid path element.\", pos, dimensionId));\n                } else {\n                    elements.add(SidedPathElement.of(pathElement, side));\n                }\n            }\n        }\n    }\n\n    public List<SidedPathElementParams> toParams() {\n        List<SidedPathElementParams> list = Lists.newArrayList();\n        for(ISidedPathElement e : elements) {\n            list.add(e.getParams());\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/path/PathFinder.java",
    "content": "package org.cyclops.integrateddynamics.core.path;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\n\nimport java.util.ArrayDeque;\nimport java.util.HashSet;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.TreeSet;\n\n\n/**\n * Algorithm to construct paths/clusters of {@link IPathElement}s.\n * @author rubensworks\n */\npublic final class PathFinder {\n\n    protected static Set<ISidedPathElement> getConnectedElements(ISidedPathElement head) {\n        Set<ISidedPathElement> connectedElements = new HashSet<>();\n        Set<DimPos> visitedPositions = new HashSet<>();\n\n        // Use a queue for BFS traversal.\n        Queue<ISidedPathElement> queue = new ArrayDeque<>();\n        DimPos headPos = head.getPathElement().getPosition();\n        visitedPositions.add(headPos);\n        queue.offer(head);\n\n        while (!queue.isEmpty()) {\n            ISidedPathElement current = queue.poll();\n            connectedElements.add(current);\n            IPathElement currentElement = current.getPathElement();\n\n            for (ISidedPathElement neighbour : currentElement.getReachableElements()) {\n                DimPos neighbourPos = neighbour.getPathElement().getPosition();\n                if (visitedPositions.add(neighbourPos)) { // Adds and returns true if not already present.\n                    queue.offer(neighbour);\n                }\n            }\n        }\n\n        return connectedElements;\n    }\n\n    public static Cluster getConnectedCluster(ISidedPathElement head) {\n        return new Cluster(new TreeSet<>(getConnectedElements(head)));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/persist/world/LabelsWorldStorage.java",
    "content": "package org.cyclops.integrateddynamics.core.persist.world;\n\nimport com.google.common.collect.Maps;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.level.saveddata.SavedDataType;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.common.util.NeoForgeExtraCodecs;\nimport net.neoforged.neoforge.event.entity.player.PlayerEvent;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.persist.world.WorldStorage;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.network.packet.ActionLabelPacket;\nimport org.cyclops.integrateddynamics.network.packet.AllLabelsPacket;\n\nimport javax.annotation.Nonnull;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * World NBT storage for variable labels.\n * Available client- and serverside and correctly synced.\n * @author rubensworks\n */\npublic class LabelsWorldStorage extends WorldStorage<LabelsWorldStorage> {\n\n    private final Map<Integer, String> labels;\n\n    public LabelsWorldStorage(Map<Integer, String> labels) {\n        this.labels = Maps.newHashMap(labels);\n        NeoForge.EVENT_BUS.register(this);\n    }\n\n    public Map<Integer, String> getLabels() {\n        return labels;\n    }\n\n    /**\n     * Put a onLabelPacket mapping for a variable id getting a onLabelPacket.\n     * Should only be called from within packets.\n     * @param variableId The variable id.\n     * @param label The onLabelPacket\n     */\n    public synchronized void putUnsafe(int variableId, @Nonnull String label) {\n        Objects.requireNonNull(label);\n        labels.put(variableId, label);\n        setDirty();\n    }\n\n    /**\n     * Remove a onLabelPacket mapping by variable id.\n     * Should only be called from within packets.\n     * @param variableId The variable id.\n     */\n    public synchronized void removeUnsafe(int variableId) {\n        labels.remove(variableId);\n        setDirty();\n    }\n\n    /**\n     * Put a onLabelPacket mapping for a variable id getting a onLabelPacket.\n     * @param variableId The variable id.\n     * @param label The onLabelPacket\n     */\n    public void put(int variableId, @Nonnull String label) {\n        if(IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(new ActionLabelPacket(variableId, label));\n        } else {\n            putUnsafe(variableId, label);\n            IntegratedDynamics._instance.getPacketHandler().sendToAll(new ActionLabelPacket(variableId, label));\n        }\n    }\n\n    /**\n     * Remove a onLabelPacket mapping by variable id.\n     * @param variableId The variable id.\n     */\n    public void remove(int variableId) {\n        if(IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            IntegratedDynamics._instance.getPacketHandler().sendToServer(new ActionLabelPacket(variableId, null));\n        } else {\n            removeUnsafe(variableId);\n            IntegratedDynamics._instance.getPacketHandler().sendToAll(new ActionLabelPacket(variableId, null));\n        }\n    }\n\n    /**\n     * Get a onLabelPacket by variable id.\n     * @param variableId The variable id.\n     * @return The corresponding variable onLabelPacket or null.\n     */\n    public synchronized String getLabel(int variableId) {\n        if (variableId < 0) {\n            return null;\n        }\n        return labels.get(variableId);\n    }\n\n    @SubscribeEvent\n    public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {\n        if(!IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n            try {\n                IntegratedDynamics._instance.getPacketHandler().sendToPlayer(new AllLabelsPacket(this.labels), (ServerPlayer) event.getEntity());\n            } catch (Exception e) {\n                // Ignore if packet cannot be sent (e.g., mock players in game tests have no connection)\n            }\n        }\n    }\n\n    @Override\n    public void afterLoad() {\n        super.afterLoad();\n        // Fix all null labels (#1038)\n        // This should not be able to occur, but it does, no idea why...\n        int sizeBefore = labels.size();\n        labels.entrySet().removeIf(integerStringEntry -> integerStringEntry.getValue() == null);\n        if (sizeBefore != labels.size()) {\n            setDirty();\n        }\n    }\n\n    public void clear() {\n        labels.clear();\n    }\n\n    public static class Access extends WorldStorage.Access<LabelsWorldStorage> {\n\n        private static LabelsWorldStorage.Access INSTANCE = null;\n        private LabelsWorldStorage clientInstance;\n\n        public static LabelsWorldStorage.Access getInstance(ModBaseNeoForge mod) {\n            if(INSTANCE == null) {\n                INSTANCE = new LabelsWorldStorage.Access(mod);\n            }\n            return INSTANCE;\n        }\n\n        public Access(ModBaseNeoForge<?> mod) {\n            super(new SavedDataType<LabelsWorldStorage>(\n                    Identifier.fromNamespaceAndPath(mod.getModId(), \"labels\"),\n                    (ctx) -> new LabelsWorldStorage(Maps.newHashMap()),\n                    ctx -> RecordCodecBuilder.create(instance -> instance.group(\n                            RecordCodecBuilder.point(ctx.getLevel()),\n                            NeoForgeExtraCodecs.unboundedMapAsList(\"k\", Codec.INT, \"v\", Codec.STRING).fieldOf(\"counters\").forGetter(data -> data.getLabels())\n                    ).apply(instance, (level, labels) -> new LabelsWorldStorage(labels)))\n            ), mod);\n        }\n\n        @Override\n        public LabelsWorldStorage get() {\n            if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n                if (clientInstance == null) {\n                    clientInstance = new LabelsWorldStorage(Maps.newHashMap());\n                }\n                return clientInstance;\n            }\n            return super.get();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/persist/world/NetworkWorldStorage.java",
    "content": "package org.cyclops.integrateddynamics.core.persist.world;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.saveddata.SavedDataType;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.persist.world.WorldStorage;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.core.TickHandler;\nimport org.cyclops.integrateddynamics.core.network.Network;\nimport org.cyclops.integrateddynamics.core.network.NetworkParams;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n/**\n * World NBT storage for all active networks.\n * @author rubensworks\n */\npublic class NetworkWorldStorage extends WorldStorage<NetworkWorldStorage> {\n\n    private final List<NetworkParams> networkParams;\n    private Set<Network> networks = Sets.newHashSet();\n    private Set<INetwork> unmodifiableSafeNetworks = null;\n\n    public NetworkWorldStorage(List<NetworkParams> networkParams) {\n        this.networkParams = Lists.newArrayList(networkParams);\n    }\n\n    public List<NetworkParams> getNetworkParams() {\n        return networkParams;\n    }\n\n    /**\n     * Add a network that needs persistence.\n     * @param network The network.\n     */\n    public synchronized void addNewNetwork(Network network) {\n        if (networks.add(network)) {\n            unmodifiableSafeNetworks = null;\n            setDirty();\n        }\n    }\n\n    /**\n     * Remove a network that was invalidated and does not need persistence anymore.\n     * This is allowed to be called if the network was already removed.\n     * @param network The network.\n     */\n    public synchronized void removeInvalidatedNetwork(Network network) {\n        if (networks.remove(network)) {\n            unmodifiableSafeNetworks = null;\n            setDirty();\n        }\n    }\n\n    /**\n     * @return A thread-safe copy of the current network set.\n     */\n    public synchronized Set<INetwork> getNetworks() {\n        if (unmodifiableSafeNetworks == null) {\n            unmodifiableSafeNetworks = Collections.unmodifiableSet(Sets.newHashSet(networks));\n        }\n        return unmodifiableSafeNetworks;\n    }\n\n    @Override\n    public void afterLoad() {\n        // Load from params\n        for (NetworkParams networkParam : networkParams) {\n            Network network = new Network();\n            network.fromParams(networkParam);\n            networks.add(network);\n        }\n\n        TickHandler.getInstance().ticked = false;\n        for (INetwork network : networks) {\n            network.afterServerLoad();\n        }\n    }\n\n    @Override\n    public void beforeSave() {\n        for(Network network : networks) {\n            network.beforeServerStop();\n        }\n\n        // Save to params\n        for(Network network : networks) {\n            // Save to params\n            this.networkParams.add(network.toParams());\n        }\n\n        // Consider always dirty\n        setDirty();\n    }\n\n    public static class Access extends WorldStorage.Access<NetworkWorldStorage> {\n\n        private static NetworkWorldStorage.Access INSTANCE = null;\n\n        public static NetworkWorldStorage.Access getInstance(ModBaseNeoForge<?> mod) {\n            if(INSTANCE == null) {\n                INSTANCE = new NetworkWorldStorage.Access(mod);\n            }\n            return INSTANCE;\n        }\n\n        public Access(ModBaseNeoForge<?> mod) {\n            super(new SavedDataType<NetworkWorldStorage>(\n                    Identifier.fromNamespaceAndPath(mod.getModId(), \"networks\"),\n                    (ctx) -> new NetworkWorldStorage(Lists.newArrayList()),\n                    ctx -> RecordCodecBuilder.create(instance -> instance.group(\n                            RecordCodecBuilder.point(ctx.getLevel()),\n                            Codec.list(NetworkParams.CODEC).fieldOf(\"networks\").forGetter(data -> data.getNetworkParams())\n                    ).apply(instance, (level, networkParams) -> new NetworkWorldStorage(networkParams)))\n            ), mod);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/category/RecipeBookCategoryDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.category;\n\nimport net.minecraft.world.item.crafting.RecipeBookCategory;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeBookCategoryConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the drying basin recipe book category.\n * @author rubensworks\n *\n */\npublic class RecipeBookCategoryDryingBasinConfig extends RecipeBookCategoryConfigCommon<RecipeBookCategory, IntegratedDynamics> {\n\n    public RecipeBookCategoryDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"drying_basin\",\n                createDefault()\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/category/RecipeBookCategoryMechanicalDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.category;\n\nimport net.minecraft.world.item.crafting.RecipeBookCategory;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeBookCategoryConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the mechanical drying basin recipe book category.\n * @author rubensworks\n *\n */\npublic class RecipeBookCategoryMechanicalDryingBasinConfig extends RecipeBookCategoryConfigCommon<RecipeBookCategory, IntegratedDynamics> {\n\n    public RecipeBookCategoryMechanicalDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_drying_basin\",\n                createDefault()\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/category/RecipeBookCategoryMechanicalSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.category;\n\nimport net.minecraft.world.item.crafting.RecipeBookCategory;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeBookCategoryConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the mechanical squeezer recipe book category.\n * @author rubensworks\n *\n */\npublic class RecipeBookCategoryMechanicalSqueezerConfig extends RecipeBookCategoryConfigCommon<RecipeBookCategory, IntegratedDynamics> {\n\n    public RecipeBookCategoryMechanicalSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_squeezer\",\n                createDefault()\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/category/RecipeBookCategorySqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.category;\n\nimport net.minecraft.world.item.crafting.RecipeBookCategory;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeBookCategoryConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the squeezer recipe book category.\n * @author rubensworks\n *\n */\npublic class RecipeBookCategorySqueezerConfig extends RecipeBookCategoryConfigCommon<RecipeBookCategory, IntegratedDynamics> {\n\n    public RecipeBookCategorySqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer\",\n                createDefault()\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/display/RecipeDisplayDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.display;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.flag.FeatureFlagSet;\nimport net.minecraft.world.item.crafting.display.RecipeDisplay;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.neoforged.neoforge.fluids.FluidStack;\n\n/**\n * @author rubensworks\n */\npublic record RecipeDisplayDryingBasin(\n        SlotDisplay inputIngredient,\n        FluidStack inputFluid,\n        SlotDisplay outputItem,\n        FluidStack outputFluid,\n        SlotDisplay craftingStation,\n        int duration\n) implements RecipeDisplay {\n\n    public static final MapCodec<RecipeDisplayDryingBasin> MAP_CODEC = RecordCodecBuilder.mapCodec(\n            instance -> instance.group(\n                            SlotDisplay.CODEC.fieldOf(\"input_ingredient\").forGetter(RecipeDisplayDryingBasin::inputIngredient),\n                            FluidStack.CODEC.fieldOf(\"input_fluid\").forGetter(RecipeDisplayDryingBasin::inputFluid),\n                            SlotDisplay.CODEC.fieldOf(\"output_ingredient\").forGetter(RecipeDisplayDryingBasin::outputItem),\n                            FluidStack.CODEC.fieldOf(\"output_fluid\").forGetter(RecipeDisplayDryingBasin::outputFluid),\n                            SlotDisplay.CODEC.fieldOf(\"crafting_station\").forGetter(RecipeDisplayDryingBasin::craftingStation),\n                            Codec.INT.fieldOf(\"duration\").forGetter(RecipeDisplayDryingBasin::duration)\n                    )\n                    .apply(instance, RecipeDisplayDryingBasin::new)\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeDisplayDryingBasin> STREAM_CODEC = StreamCodec.composite(\n            SlotDisplay.STREAM_CODEC,\n            RecipeDisplayDryingBasin::inputIngredient,\n            FluidStack.OPTIONAL_STREAM_CODEC,\n            RecipeDisplayDryingBasin::inputFluid,\n            SlotDisplay.STREAM_CODEC,\n            RecipeDisplayDryingBasin::outputItem,\n            FluidStack.OPTIONAL_STREAM_CODEC,\n            RecipeDisplayDryingBasin::outputFluid,\n            SlotDisplay.STREAM_CODEC,\n            RecipeDisplayDryingBasin::craftingStation,\n            ByteBufCodecs.VAR_INT,\n            RecipeDisplayDryingBasin::duration,\n            RecipeDisplayDryingBasin::new\n    );\n    public static final RecipeDisplay.Type<RecipeDisplayDryingBasin> TYPE = new RecipeDisplay.Type<>(MAP_CODEC, STREAM_CODEC);\n\n    @Override\n    public SlotDisplay result() {\n        if (outputItem() == SlotDisplay.Empty.INSTANCE) {\n            FluidStack fluidStack = outputFluid();\n            return new SlotDisplay.ItemSlotDisplay(outputFluid().getFluidType().getBucket(fluidStack).getItem());\n        }\n        return outputItem();\n    }\n\n    @Override\n    public Type<? extends RecipeDisplay> type() {\n        return TYPE;\n    }\n\n    @Override\n    public boolean isEnabled(FeatureFlagSet featureFlagSet) {\n        return this.inputIngredient.isEnabled(featureFlagSet)\n                && this.outputItem().isEnabled(featureFlagSet)\n                && RecipeDisplay.super.isEnabled(featureFlagSet);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/display/RecipeDisplayDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.display;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeDisplayConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the drying basin recipe display.\n * @author rubensworks\n *\n */\npublic class RecipeDisplayDryingBasinConfig extends RecipeDisplayConfigCommon<RecipeDisplayDryingBasin, IntegratedDynamics> {\n\n    public RecipeDisplayDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"drying_basin\",\n                eConfig -> RecipeDisplayDryingBasin.TYPE\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/display/RecipeDisplaySqueezer.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.display;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.flag.FeatureFlagSet;\nimport net.minecraft.world.item.crafting.display.RecipeDisplay;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.core.helper.Codecs;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic record RecipeDisplaySqueezer(\n        SlotDisplay inputIngredient,\n        List<Pair<? extends SlotDisplay, Float>> outputItems,\n        FluidStack outputFluid,\n        SlotDisplay craftingStation,\n        int duration\n) implements RecipeDisplay {\n\n    public static final MapCodec<RecipeDisplaySqueezer> MAP_CODEC = RecordCodecBuilder.mapCodec(\n            instance -> instance.group(\n                            SlotDisplay.CODEC.fieldOf(\"input_ingredient\").forGetter(RecipeDisplaySqueezer::inputIngredient),\n                            Codecs.SLOT_DISPLAY_CHANCE.listOf().fieldOf(\"output_ingredients\").forGetter(RecipeDisplaySqueezer::outputItems),\n                            FluidStack.CODEC.fieldOf(\"output_fluid\").forGetter(RecipeDisplaySqueezer::outputFluid),\n                            SlotDisplay.CODEC.fieldOf(\"crafting_station\").forGetter(RecipeDisplaySqueezer::craftingStation),\n                            Codec.INT.fieldOf(\"duration\").forGetter(RecipeDisplaySqueezer::duration)\n                    )\n                    .apply(instance, RecipeDisplaySqueezer::new)\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeDisplaySqueezer> STREAM_CODEC = StreamCodec.composite(\n            SlotDisplay.STREAM_CODEC,\n            RecipeDisplaySqueezer::inputIngredient,\n            Codecs.STREAM_SLOT_DISPLAY_CHANCE.apply(ByteBufCodecs.list()),\n            RecipeDisplaySqueezer::outputItems,\n            FluidStack.OPTIONAL_STREAM_CODEC,\n            RecipeDisplaySqueezer::outputFluid,\n            SlotDisplay.STREAM_CODEC,\n            RecipeDisplaySqueezer::craftingStation,\n            ByteBufCodecs.VAR_INT,\n            RecipeDisplaySqueezer::duration,\n            RecipeDisplaySqueezer::new\n    );\n    public static final Type<RecipeDisplaySqueezer> TYPE = new Type<>(MAP_CODEC, STREAM_CODEC);\n\n    @Override\n    public SlotDisplay result() {\n        if (outputItems().isEmpty()) {\n            FluidStack fluidStack = outputFluid();\n            return new SlotDisplay.ItemSlotDisplay(outputFluid().getFluidType().getBucket(fluidStack).getItem());\n        }\n        return outputItems().getFirst().getLeft();\n    }\n\n    @Override\n    public Type<? extends RecipeDisplay> type() {\n        return TYPE;\n    }\n\n    @Override\n    public boolean isEnabled(FeatureFlagSet featureFlagSet) {\n        return this.inputIngredient.isEnabled(featureFlagSet)\n                && this.outputItems.stream().allMatch(pair -> pair.getLeft().isEnabled(featureFlagSet))\n                && RecipeDisplay.super.isEnabled(featureFlagSet);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/display/RecipeDisplaySqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.display;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeDisplayConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the squeezer recipe display.\n * @author rubensworks\n *\n */\npublic class RecipeDisplaySqueezerConfig extends RecipeDisplayConfigCommon<RecipeDisplaySqueezer, IntegratedDynamics> {\n\n    public RecipeDisplaySqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer\",\n                eConfig -> RecipeDisplaySqueezer.TYPE\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/handler/RecipeHandlerDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.handler;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.commoncapabilities.api.capability.fluidhandler.FluidMatch;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;\nimport org.cyclops.cyclopscore.ingredient.recipe.IngredientRecipeHelpers;\nimport org.cyclops.cyclopscore.ingredient.recipe.RecipeHandlerRecipeType;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.cyclopscore.recipe.type.InventoryFluid;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeDryingBasin;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\n/**\n * @author rubensworks\n */\npublic class RecipeHandlerDryingBasin<T extends RecipeDryingBasin> extends RecipeHandlerRecipeType<IInventoryFluid, T> {\n\n    public RecipeHandlerDryingBasin(Supplier<Level> worldSupplier, RecipeType<T> recipeType) {\n        super(worldSupplier,\n                recipeType,\n                Sets.newHashSet(IngredientComponent.ITEMSTACK, IngredientComponent.FLUIDSTACK),\n                Sets.newHashSet(IngredientComponent.ITEMSTACK, IngredientComponent.FLUIDSTACK));\n    }\n\n    @Nullable\n    @Override\n    protected IInventoryFluid getRecipeInputContainer(IMixedIngredients input) {\n        InventoryFluid inventory = new InventoryFluid(NonNullList.withSize(1, ItemStack.EMPTY), NonNullList.withSize(1, FluidStack.EMPTY));\n        if (!input.getInstances(IngredientComponent.ITEMSTACK).isEmpty()) {\n            inventory.setItem(0, input.getInstances(IngredientComponent.ITEMSTACK).get(0));\n        }\n        if (!input.getInstances(IngredientComponent.FLUIDSTACK).isEmpty()) {\n            FluidStack fluidStack = input.getInstances(IngredientComponent.FLUIDSTACK).get(0);\n            try (var tx = Transaction.openRoot()) {\n                inventory.getFluidHandler().insert(FluidResource.of(fluidStack), fluidStack.getAmount(), tx);\n                tx.commit();\n            }\n        }\n        return inventory;\n    }\n\n    @Nullable\n    @Override\n    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getRecipeInputIngredients(RecipeDryingBasin recipe) {\n        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();\n        if (recipe.getInputIngredient().isPresent()) {\n            inputs.put(IngredientComponent.ITEMSTACK, Lists.newArrayList(IngredientRecipeHelpers.getPrototypesFromIngredient(recipe.getInputIngredient().get())));\n        }\n        if (recipe.getInputFluid().isPresent()) {\n            inputs.put(IngredientComponent.FLUIDSTACK, Lists.newArrayList(new PrototypedIngredientAlternativesList<>(\n                    Lists.newArrayList(new PrototypedIngredient<>(IngredientComponent.FLUIDSTACK, recipe.getInputFluid().get(), FluidMatch.EXACT)))));\n        }\n        return inputs;\n    }\n\n    @Nullable\n    @Override\n    protected IMixedIngredients getRecipeOutputIngredients(RecipeDryingBasin recipe) {\n        Map<IngredientComponent<?, ?>, List<?>> outputIngredients = Maps.newIdentityHashMap();\n        if (recipe.getOutputItemFirst().isPresent()) {\n            outputIngredients.put(IngredientComponent.ITEMSTACK, Lists.newArrayList(recipe.getOutputItemFirst().get()));\n        }\n        if (!recipe.getOutputFluid().isEmpty()) {\n            outputIngredients.put(IngredientComponent.FLUIDSTACK, Lists.newArrayList(recipe.getOutputFluid().get()));\n        }\n\n        // Validate output\n        if (outputIngredients.isEmpty()) {\n            return null;\n        }\n\n        return new MixedIngredients(outputIngredients);\n    }\n\n    @Override\n    public boolean isValidSizeInput(IngredientComponent<?, ?> ingredientComponent, int size) {\n        return (ingredientComponent == IngredientComponent.ITEMSTACK || ingredientComponent == IngredientComponent.FLUIDSTACK)\n                && size == 1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/handler/RecipeHandlerSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.handler;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.ingredient.recipe.IngredientRecipeHelpers;\nimport org.cyclops.cyclopscore.ingredient.recipe.RecipeHandlerRecipeType;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeSqueezer;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\n/**\n * @author rubensworks\n */\npublic class RecipeHandlerSqueezer<T extends RecipeSqueezer> extends RecipeHandlerRecipeType<CraftingInput, T> {\n\n    public RecipeHandlerSqueezer(Supplier<Level> worldSupplier, RecipeType<T> recipeType) {\n        super(worldSupplier,\n                recipeType,\n                Sets.newHashSet(IngredientComponent.ITEMSTACK),\n                Sets.newHashSet(IngredientComponent.ITEMSTACK, IngredientComponent.FLUIDSTACK));\n    }\n\n    @Nullable\n    @Override\n    protected CraftingInput getRecipeInputContainer(IMixedIngredients input) {\n        return CraftingInput.of(1, 1, Lists.newArrayList(input.getInstances(IngredientComponent.ITEMSTACK).get(0)));\n    }\n\n    @Nullable\n    @Override\n    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getRecipeInputIngredients(RecipeSqueezer recipe) {\n        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();\n        inputs.put(IngredientComponent.ITEMSTACK, Lists.newArrayList(IngredientRecipeHelpers.getPrototypesFromIngredient(recipe.getInputIngredient())));\n        return inputs;\n    }\n\n    @Nullable\n    @Override\n    protected IMixedIngredients getRecipeOutputIngredients(RecipeSqueezer recipe) {\n        Map<IngredientComponent<?, ?>, List<?>> outputIngredients = Maps.newIdentityHashMap();\n        List<ItemStack> outputItems = recipe.getOutputItems().stream()\n                .filter(i -> i.getChance() == 1.0F)\n                .map(RecipeSqueezer.IngredientChance::getIngredientFirst)\n                .collect(Collectors.toList());\n        if (!outputItems.isEmpty()) {\n            outputIngredients.put(IngredientComponent.ITEMSTACK, outputItems);\n        }\n        if (!recipe.getOutputFluid().isEmpty()) {\n            outputIngredients.put(IngredientComponent.FLUIDSTACK, Lists.newArrayList(recipe.getOutputFluid().get()));\n        }\n\n        // Validate output\n        if (outputIngredients.isEmpty()) {\n            return null;\n        }\n\n        return new MixedIngredients(outputIngredients);\n    }\n\n    @Override\n    public boolean isValidSizeInput(IngredientComponent<?, ?> ingredientComponent, int size) {\n        return ingredientComponent == IngredientComponent.ITEMSTACK && size == 1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.datafixers.util.Either;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.ItemStackTemplate;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.item.crafting.display.RecipeDisplay;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.cyclopscore.recipe.ItemStackFromIngredient;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplayDryingBasin;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Drying basin recipe\n * @author rubensworks\n */\npublic class RecipeDryingBasin implements Recipe<IInventoryFluid> {\n\n    private final Optional<Ingredient> inputIngredient;\n    private final Optional<FluidStackTemplate> inputFluid;\n    private final Optional<Either<ItemStackTemplate, ItemStackFromIngredient>> outputItem;\n    private final Optional<FluidStackTemplate> outputFluid;\n    private final int duration;\n\n    private PlacementInfo placementInfo;\n\n    public RecipeDryingBasin(Optional<Ingredient> inputIngredient, Optional<FluidStackTemplate> inputFluid,\n                             Optional<Either<ItemStackTemplate, ItemStackFromIngredient>> outputIngredient, Optional<FluidStackTemplate> outputFluid, int duration) {\n        this.inputIngredient = inputIngredient;\n        this.inputFluid = inputFluid;\n        this.outputItem = outputIngredient;\n        this.outputFluid = outputFluid;\n        this.duration = duration;\n    }\n\n    public Optional<Ingredient> getInputIngredient() {\n        return inputIngredient;\n    }\n\n    public Optional<FluidStackTemplate> getInputFluidTemplate() {\n        return inputFluid;\n    }\n\n    public Optional<FluidStack> getInputFluid() {\n        return inputFluid.map(FluidStackTemplate::create);\n    }\n\n    public Optional<Either<ItemStackTemplate, ItemStackFromIngredient>> getOutputItemTemplate() {\n        return outputItem;\n    }\n\n    public Optional<Either<ItemStack, ItemStackFromIngredient>> getOutputItem() {\n        return outputItem.map(either -> either.map(\n                t -> Either.left(t.create()),\n                Either::right));\n    }\n\n    public Optional<FluidStackTemplate> getOutputFluidTemplate() {\n        return outputFluid;\n    }\n\n    public Optional<FluidStack> getOutputFluid() {\n        return outputFluid.map(FluidStackTemplate::create);\n    }\n\n    public Optional<ItemStack> getOutputItemFirst() {\n        return outputItem.map(either -> either.map(ItemStackTemplate::create, ItemStackFromIngredient::getFirstItemStack));\n    }\n\n    public int getDuration() {\n        return duration;\n    }\n\n    @Override\n    public boolean matches(IInventoryFluid inv, Level worldIn) {\n        return inputIngredient.map(p -> p.test(inv.getItem(0))).orElse(inv.getItem(0).isEmpty())\n                && inputFluid.map(f -> f.fluid().value() == inv.getFluidHandler().getResource(0).getFluid()).orElse(inv.getFluidHandler().getResource(0).isEmpty())\n                && inputFluid.map(f -> f.amount() <= inv.getFluidHandler().getAmountAsInt(0)).orElse(inv.getFluidHandler().getAmountAsInt(0) == 0);\n    }\n\n    @Override\n    public ItemStack assemble(IInventoryFluid inv) {\n        return this.getOutputItemFirst().get().copy();\n    }\n\n    @Override\n    public String group() {\n        return \"\";\n    }\n\n    @Override\n    public boolean showNotification() {\n        return false;\n    }\n\n    @Override\n    public RecipeSerializer<? extends Recipe<IInventoryFluid>> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_DRYING_BASIN.get();\n    }\n\n    @Override\n    public RecipeType<? extends Recipe<IInventoryFluid>> getType() {\n        return RegistryEntries.RECIPETYPE_DRYING_BASIN.get();\n    }\n\n    @Override\n    public PlacementInfo placementInfo() {\n        if (this.placementInfo == null) {\n            this.placementInfo = PlacementInfo.create(this.inputIngredient.orElse(Ingredient.of(Items.BUCKET)));\n        }\n        return this.placementInfo;\n    }\n\n    @Override\n    public RecipeBookCategory recipeBookCategory() {\n        return RegistryEntries.RECIPEBOOKCATEGORY_DRYING_BASIN.get();\n    }\n\n    @Override\n    public List<RecipeDisplay> display() {\n        return List.of(new RecipeDisplayDryingBasin(\n                this.getInputIngredient().map(Ingredient::display).orElse(SlotDisplay.Empty.INSTANCE),\n                this.getInputFluid().orElse(FluidStack.EMPTY),\n                this.getOutputItemFirst().<SlotDisplay>map(stack -> new SlotDisplay.ItemStackSlotDisplay(net.minecraft.world.item.ItemStackTemplate.fromNonEmptyStack(stack))).orElse(SlotDisplay.Empty.INSTANCE),\n                this.getOutputFluid().orElse(FluidStack.EMPTY),\n                new SlotDisplay.ItemSlotDisplay(RegistryEntries.BLOCK_DRYING_BASIN.get().asItem()),\n                this.getDuration()\n        ));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeEnergyContainerCombination.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.capability.energystorage.IEnergyStorageCapacity;\nimport org.cyclops.integrateddynamics.capability.energystorage.IEnergyStorageMutable;\n\n/**\n * Recipe for combining energy batteries in a shapeless manner.\n * @author rubensworks\n */\npublic class RecipeEnergyContainerCombination extends CustomRecipe {\n\n    private final Ingredient batteryItem;\n    private final int maxCapacity;\n\n    public RecipeEnergyContainerCombination(Ingredient batteryItem, int maxCapacity) {\n        super();\n        this.batteryItem = batteryItem;\n        this.maxCapacity = maxCapacity;\n    }\n\n    public Ingredient getBatteryItem() {\n        return batteryItem;\n    }\n\n    public int getMaxCapacity() {\n        return maxCapacity;\n    }\n\n    @Override\n    public boolean matches(CraftingInput grid, Level world) {\n        return !assemble(grid).isEmpty();\n    }\n\n    protected ItemStack getResultItem() {\n        return new ItemStack(this.batteryItem.items().findFirst().get());\n    }\n\n    @Override\n    public NonNullList<ItemStack> getRemainingItems(CraftingInput inventory) {\n        NonNullList<ItemStack> aitemstack = NonNullList.withSize(inventory.size(), ItemStack.EMPTY);\n\n        for (int i = 0; i < aitemstack.size(); ++i) {\n            ItemStack itemstack = inventory.getItem(i);\n            net.minecraft.world.item.ItemStackTemplate remainder = itemstack.getItem().getCraftingRemainder(itemstack);\n            aitemstack.set(i, remainder != null ? remainder.create() : ItemStack.EMPTY);\n        }\n\n        return aitemstack;\n    }\n\n    @Override\n    public RecipeSerializer<? extends CustomRecipe> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_ENERGY_CONTAINER_COMBINATION.get();\n    }\n\n    @Override\n    public ItemStack assemble(CraftingInput grid) {\n        ItemStack output = getResultItem().copy();\n        IEnergyStorageCapacity energyStorage = (IEnergyStorageCapacity) output.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(output));\n\n        int totalCapacity = 0;\n        int totalEnergy = 0;\n        int inputItems = 0;\n\n        // Loop over the grid and count the total contents and capacity + collect energy.\n        for(int j = 0; j < grid.size(); j++) {\n            ItemStack element = grid.getItem(j).copy().split(1);\n            if(!element.isEmpty()) {\n                if(this.batteryItem.test(element)) {\n                    IEnergyStorageCapacity currentEnergyStorage = (IEnergyStorageCapacity) element.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(element));\n                    inputItems++;\n                    totalEnergy = IModHelpers.get().getBaseHelpers().addSafe(totalEnergy, currentEnergyStorage.getAmountAsInt());\n                    totalCapacity = IModHelpers.get().getBaseHelpers().addSafe(totalCapacity, currentEnergyStorage.getCapacityAsInt());\n                } else {\n                    return ItemStack.EMPTY;\n                }\n            }\n        }\n\n        if(inputItems < 2\n                || totalCapacity > this.maxCapacity) {\n            return ItemStack.EMPTY;\n        }\n\n        // Set capacity and fill fluid into output.\n        energyStorage.setCapacity(totalCapacity);\n        ((IEnergyStorageMutable) energyStorage).setEnergy(totalEnergy);\n\n        return output;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeEnergyContainerCombinationConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link RecipeEnergyContainerCombination}.\n * @author rubensworks\n */\npublic class RecipeEnergyContainerCombinationConfig extends RecipeConfigCommon<RecipeEnergyContainerCombination, IntegratedDynamics> {\n\n    public RecipeEnergyContainerCombinationConfig() {\n        super(IntegratedDynamics._instance,\n                String.format(\"crafting_special_energycontainer_combination\"),\n                eConfig -> RecipeSerializerEnergyContainerCombination.SERIALIZER);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.datafixers.util.Either;\nimport net.minecraft.world.item.ItemStackTemplate;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.item.crafting.display.RecipeDisplay;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.cyclopscore.recipe.ItemStackFromIngredient;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplayDryingBasin;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Mechanical drying basin recipe\n * @author rubensworks\n */\npublic class RecipeMechanicalDryingBasin extends RecipeDryingBasin {\n\n    public RecipeMechanicalDryingBasin(Optional<Ingredient> inputIngredient, Optional<FluidStackTemplate> inputFluid,\n                                       Optional<Either<ItemStackTemplate, ItemStackFromIngredient>> outputItem, Optional<FluidStackTemplate> outputFluid, int duration) {\n        super(inputIngredient, inputFluid, outputItem, outputFluid, duration);\n    }\n\n    @Override\n    public RecipeSerializer<? extends Recipe<IInventoryFluid>> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_MECHANICAL_DRYING_BASIN.get();\n    }\n\n    @Override\n    public RecipeType<? extends Recipe<IInventoryFluid>> getType() {\n        return RegistryEntries.RECIPETYPE_MECHANICAL_DRYING_BASIN.get();\n    }\n\n    @Override\n    public RecipeBookCategory recipeBookCategory() {\n        return RegistryEntries.RECIPEBOOKCATEGORY_MECHANICAL_DRYING_BASIN.get();\n    }\n\n    @Override\n    public List<RecipeDisplay> display() {\n        return List.of(new RecipeDisplayDryingBasin(\n                this.getInputIngredient().map(Ingredient::display).orElse(SlotDisplay.Empty.INSTANCE),\n                this.getInputFluid().orElse(FluidStack.EMPTY),\n                this.getOutputItemFirst().<SlotDisplay>map(stack -> new SlotDisplay.ItemStackSlotDisplay(net.minecraft.world.item.ItemStackTemplate.fromNonEmptyStack(stack))).orElse(SlotDisplay.Empty.INSTANCE),\n                this.getOutputFluid().orElse(FluidStack.EMPTY),\n                new SlotDisplay.ItemSlotDisplay(RegistryEntries.BLOCK_MECHANICAL_DRYING_BASIN.get().asItem()),\n                this.getDuration()\n        ));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.item.crafting.display.RecipeDisplay;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplaySqueezer;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Mechanical squeezer recipe\n * @author rubensworks\n */\npublic class RecipeMechanicalSqueezer extends RecipeSqueezer {\n\n    private final int duration;\n\n    public RecipeMechanicalSqueezer(Ingredient inputIngredient,\n                                    NonNullList<IngredientChance> outputItems,\n                                    Optional<FluidStackTemplate> outputFluid,\n                                    int duration) {\n        super(inputIngredient, outputItems, outputFluid);\n        this.duration = duration;\n    }\n\n    public int getDuration() {\n        return duration;\n    }\n\n    @Override\n    public RecipeSerializer<? extends Recipe<CraftingInput>> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_MECHANICAL_SQUEEZER.get();\n    }\n\n    @Override\n    public RecipeType<? extends Recipe<CraftingInput>> getType() {\n        return RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER.get();\n    }\n\n    @Override\n    public RecipeBookCategory recipeBookCategory() {\n        return RegistryEntries.RECIPEBOOKCATEGORY_MECHANICAL_SQUEEZER.get();\n    }\n\n    @Override\n    public List<RecipeDisplay> display() {\n        return List.of(new RecipeDisplaySqueezer(\n                this.getInputIngredient().display(),\n                this.getOutputItemsAsSlots(),\n                this.getOutputFluid().orElse(FluidStack.EMPTY),\n                new SlotDisplay.ItemSlotDisplay(RegistryEntries.BLOCK_MECHANICAL_SQUEEZER.get().asItem()),\n                this.getDuration()\n        ));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeMechanicalSqueezerFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.Recipe;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.item.ItemFacade;\n\nimport java.util.Optional;\n\npublic class RecipeMechanicalSqueezerFacade extends RecipeMechanicalSqueezer {\n\n    public RecipeMechanicalSqueezerFacade(int duration) {\n        super(Ingredient.of(RegistryEntries.ITEM_FACADE.get()), NonNullList.of(RecipeSqueezerFacade.OUTPUT, RecipeSqueezerFacade.OUTPUT), Optional.empty(), duration);\n    }\n\n    @Override\n    public NonNullList<IngredientChance> assemble(ItemStack inputItem) {\n      return RecipeSqueezerFacade.getOutput(inputItem);\n    }\n\n    @Override\n    public boolean matches(CraftingInput inv, Level worldIn) {\n        return ((inv.getItem(0).getItem() instanceof ItemFacade) && (inv.getItem(0).has(RegistryEntries.DATACOMPONENT_FACADE_BLOCK)));\n    }\n\n    @Override\n    public RecipeSerializer<? extends Recipe<CraftingInput>> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_MECHANICAL_SQUEEZER_FACADE.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeNbtClear.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Crafting recipe to clear item NBT data.\n * @author rubensworks\n */\npublic class RecipeNbtClear extends CustomRecipe {\n\n    private final Ingredient inputIngredient;\n\n    public RecipeNbtClear(Ingredient inputIngredient) {\n        super();\n        this.inputIngredient = inputIngredient;\n    }\n\n    public Ingredient getInputIngredient() {\n        return inputIngredient;\n    }\n\n    @Override\n    public boolean matches(CraftingInput inv, Level worldIn) {\n        return !assemble(inv).isEmpty();\n    }\n\n    @Override\n    public ItemStack assemble(CraftingInput inv) {\n        ItemStack ret = ItemStack.EMPTY;\n        for(int j = 0; j < inv.size(); j++) {\n            ItemStack element = inv.getItem(j);\n            if(!element.isEmpty()) {\n                if (this.inputIngredient.test(element)) {\n                    if (!ret.isEmpty()) {\n                        return ItemStack.EMPTY;\n                    }\n                    // Create copy of the stack WITHOUT the NBT tag.\n                    ret = new ItemStack(element.getItem());\n                } else {\n                    return ItemStack.EMPTY;\n                }\n            }\n        }\n        return ret;\n    }\n\n    public ItemStack getResultItem() {\n        return new ItemStack(inputIngredient.items().findFirst().get()); // This is just a dummy item!\n    }\n\n    @Override\n    public NonNullList<ItemStack> getRemainingItems(CraftingInput inv) {\n        return NonNullList.withSize(inv.size(), ItemStack.EMPTY);\n    }\n\n    @Override\n    public boolean isSpecial() {\n        return true;\n    }\n\n    @Override\n    public RecipeSerializer<? extends CustomRecipe> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_NBT_CLEAR.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.google.gson.JsonSyntaxException;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.cyclopscore.helper.RecipeSerializerHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\n\n/**\n * Recipe serializer for drying basin recipes\n * @author rubensworks\n */\npublic class RecipeSerializerDryingBasin {\n\n    public static final MapCodec<RecipeDryingBasin> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Ingredient.CODEC.optionalFieldOf(\"input_item\").forGetter(RecipeDryingBasin::getInputIngredient),\n                            FluidStackTemplate.CODEC.optionalFieldOf(\"input_fluid\").forGetter(RecipeDryingBasin::getInputFluidTemplate),\n                            RecipeSerializerHelpers.getCodecItemStackTemplateOrTag(() -> GeneralConfig.recipeTagOutputModPriorities).optionalFieldOf(\"output_item\").forGetter(RecipeDryingBasin::getOutputItemTemplate),\n                            FluidStackTemplate.CODEC.optionalFieldOf(\"output_fluid\").forGetter(RecipeDryingBasin::getOutputFluidTemplate),\n                            Codec.INT.fieldOf(\"duration\").forGetter(RecipeDryingBasin::getDuration)\n                    )\n                    .apply(builder, (inputItem, inputFluid, outputItem, outputFluid, duration) -> {\n                        // Validation\n                        if (inputItem.isEmpty() && inputFluid.isEmpty()) {\n                            throw new JsonSyntaxException(\"An input item or fluid is required\");\n                        }\n                        if (outputItem.isEmpty() && outputFluid.isEmpty()) {\n                            throw new JsonSyntaxException(\"An output item or fluid is required\");\n                        }\n                        if (inputFluid.isPresent() && outputFluid.isPresent()) {\n                            throw new JsonSyntaxException(\"Can't have both an input and output fluid\");\n                        }\n                        if (duration <= 0) {\n                            throw new JsonSyntaxException(\"Durations must be higher than one tick\");\n                        }\n\n                        return new RecipeDryingBasin(inputItem, inputFluid, outputItem, outputFluid, duration);\n                    })\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeDryingBasin> STREAM_CODEC = StreamCodec.composite(\n            ByteBufCodecs.optional(Ingredient.CONTENTS_STREAM_CODEC), RecipeDryingBasin::getInputIngredient,\n            ByteBufCodecs.optional(FluidStackTemplate.STREAM_CODEC), RecipeDryingBasin::getInputFluidTemplate,\n            ByteBufCodecs.optional(RecipeSerializerHelpers.STREAM_CODEC_ITEMSTACKTEMPLATE_OR_TAG), RecipeDryingBasin::getOutputItemTemplate,\n            ByteBufCodecs.optional(FluidStackTemplate.STREAM_CODEC), RecipeDryingBasin::getOutputFluidTemplate,\n            ByteBufCodecs.INT, RecipeDryingBasin::getDuration,\n            RecipeDryingBasin::new\n    );\n    public static final RecipeSerializer<RecipeDryingBasin> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the drying basin recipe serializer.\n * @author rubensworks\n *\n */\npublic class RecipeSerializerDryingBasinConfig extends RecipeConfigCommon<RecipeDryingBasin, IntegratedDynamics> {\n\n    public RecipeSerializerDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"drying_basin\",\n                eConfig -> RecipeSerializerDryingBasin.SERIALIZER\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerEnergyContainerCombination.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\n\n/**\n * Recipe serializer for energy container combinations.\n * @author rubensworks\n */\npublic class RecipeSerializerEnergyContainerCombination {\n\n    public static final MapCodec<RecipeEnergyContainerCombination> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Ingredient.CODEC.fieldOf(\"item\").forGetter(RecipeEnergyContainerCombination::getBatteryItem),\n                            Codec.INT.fieldOf(\"maxCapacity\").forGetter(RecipeEnergyContainerCombination::getMaxCapacity)\n                    )\n                    .apply(builder, RecipeEnergyContainerCombination::new)\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeEnergyContainerCombination> STREAM_CODEC = StreamCodec.composite(\n            Ingredient.CONTENTS_STREAM_CODEC, RecipeEnergyContainerCombination::getBatteryItem,\n            ByteBufCodecs.INT, RecipeEnergyContainerCombination::getMaxCapacity,\n            RecipeEnergyContainerCombination::new\n    );\n    public static final RecipeSerializer<RecipeEnergyContainerCombination> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.google.gson.JsonSyntaxException;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.cyclopscore.helper.RecipeSerializerHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\n\n/**\n * Recipe serializer for mechanical drying basin recipes\n * @author rubensworks\n */\npublic class RecipeSerializerMechanicalDryingBasin {\n\n    public static final MapCodec<RecipeMechanicalDryingBasin> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Ingredient.CODEC.optionalFieldOf(\"input_item\").forGetter(RecipeDryingBasin::getInputIngredient),\n                            FluidStackTemplate.CODEC.optionalFieldOf(\"input_fluid\").forGetter(RecipeDryingBasin::getInputFluidTemplate),\n                            RecipeSerializerHelpers.getCodecItemStackTemplateOrTag(() -> GeneralConfig.recipeTagOutputModPriorities).optionalFieldOf(\"output_item\").forGetter(RecipeDryingBasin::getOutputItemTemplate),\n                            FluidStackTemplate.CODEC.optionalFieldOf(\"output_fluid\").forGetter(RecipeDryingBasin::getOutputFluidTemplate),\n                            Codec.INT.fieldOf(\"duration\").forGetter(RecipeDryingBasin::getDuration)\n                    )\n                    .apply(builder, (inputItem, inputFluid, outputItem, outputFluid, duration) -> {\n                        // Validation\n                        if (inputItem.isEmpty() && inputFluid.isEmpty()) {\n                            throw new JsonSyntaxException(\"An input item or fluid is required\");\n                        }\n                        if (outputItem.isEmpty() && outputFluid.isEmpty()) {\n                            throw new JsonSyntaxException(\"An output item or fluid is required\");\n                        }\n                        if (inputFluid.isPresent() && outputFluid.isPresent()) {\n                            throw new JsonSyntaxException(\"Can't have both an input and output fluid\");\n                        }\n                        if (duration <= 0) {\n                            throw new JsonSyntaxException(\"Durations must be higher than one tick\");\n                        }\n\n                        return new RecipeMechanicalDryingBasin(inputItem, inputFluid, outputItem, outputFluid, duration);\n                    })\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeMechanicalDryingBasin> STREAM_CODEC = StreamCodec.composite(\n            ByteBufCodecs.optional(Ingredient.CONTENTS_STREAM_CODEC), RecipeDryingBasin::getInputIngredient,\n            ByteBufCodecs.optional(FluidStackTemplate.STREAM_CODEC), RecipeDryingBasin::getInputFluidTemplate,\n            ByteBufCodecs.optional(RecipeSerializerHelpers.STREAM_CODEC_ITEMSTACKTEMPLATE_OR_TAG), RecipeDryingBasin::getOutputItemTemplate,\n            ByteBufCodecs.optional(FluidStackTemplate.STREAM_CODEC), RecipeDryingBasin::getOutputFluidTemplate,\n            ByteBufCodecs.INT, RecipeDryingBasin::getDuration,\n            RecipeMechanicalDryingBasin::new\n    );\n    public static final RecipeSerializer<RecipeMechanicalDryingBasin> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerMechanicalDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the mechanical drying basin recipe serializer.\n * @author rubensworks\n *\n */\npublic class RecipeSerializerMechanicalDryingBasinConfig extends RecipeConfigCommon<RecipeMechanicalDryingBasin, IntegratedDynamics> {\n\n    public RecipeSerializerMechanicalDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_drying_basin\",\n                eConfig -> RecipeSerializerMechanicalDryingBasin.SERIALIZER\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.google.gson.JsonSyntaxException;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.cyclopscore.codec.ListCodecStrict;\n\nimport java.util.Optional;\n\n/**\n * Recipe serializer for mechanical squeezer recipes\n * @author rubensworks\n */\npublic class RecipeSerializerMechanicalSqueezer {\n\n    public static final MapCodec<RecipeMechanicalSqueezer> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Ingredient.CODEC.fieldOf(\"input_item\").forGetter(RecipeMechanicalSqueezer::getInputIngredient),\n                            new ListCodecStrict<>(RecipeSerializerSqueezer.CODEC_INGREDIENT_CHANCE).optionalFieldOf(\"output_items\").forGetter(r -> r.getOutputItems().isEmpty() ? Optional.empty() : Optional.of(r.getOutputItems().stream().toList())),\n                            FluidStackTemplate.CODEC.optionalFieldOf(\"output_fluid\").forGetter(RecipeMechanicalSqueezer::getOutputFluidTemplate),\n                            Codec.INT.fieldOf(\"duration\").forGetter(RecipeMechanicalSqueezer::getDuration)\n                    )\n                    .apply(builder, (inputIngredient, outputItemStacks, outputFluid, duration) -> {\n                        // Validation\n                        if (outputItemStacks.isEmpty() && outputFluid.isEmpty()) {\n                            throw new JsonSyntaxException(\"An output item or fluid is required\");\n                        }\n                        if (duration <= 0) {\n                            throw new JsonSyntaxException(\"Durations must be higher than one tick\");\n                        }\n\n                        return new RecipeMechanicalSqueezer(inputIngredient, outputItemStacks.map(NonNullList::copyOf).orElseGet(NonNullList::create), outputFluid, duration);\n                    })\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeMechanicalSqueezer> STREAM_CODEC = StreamCodec.composite(\n            Ingredient.CONTENTS_STREAM_CODEC, RecipeMechanicalSqueezer::getInputIngredient,\n            RecipeSerializerSqueezer.STREAM_CODEC_INGREDIENT_CHANCE.apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)), RecipeMechanicalSqueezer::getOutputItems,\n            ByteBufCodecs.optional(FluidStackTemplate.STREAM_CODEC), RecipeMechanicalSqueezer::getOutputFluidTemplate,\n            ByteBufCodecs.INT, RecipeMechanicalSqueezer::getDuration,\n            RecipeMechanicalSqueezer::new\n    );\n    public static final RecipeSerializer<RecipeMechanicalSqueezer> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerMechanicalSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the mechanical squeezer recipe serializer.\n * @author rubensworks\n *\n */\npublic class RecipeSerializerMechanicalSqueezerConfig extends RecipeConfigCommon<RecipeMechanicalSqueezer, IntegratedDynamics> {\n\n    public RecipeSerializerMechanicalSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_squeezer\",\n                eConfig -> RecipeSerializerMechanicalSqueezer.SERIALIZER\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerMechanicalSqueezerFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.google.gson.JsonSyntaxException;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\n\npublic class RecipeSerializerMechanicalSqueezerFacade {\n\n    public static final MapCodec<RecipeMechanicalSqueezerFacade> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Codec.INT.fieldOf(\"duration\").forGetter(RecipeMechanicalSqueezerFacade::getDuration)\n                    )\n                    .apply(builder, (duration) -> {\n                        // Validation\n                        if (duration <= 0) {\n                            throw new JsonSyntaxException(\"Durations must be higher than one tick\");\n                        }\n                        return new RecipeMechanicalSqueezerFacade(duration);\n                    })\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeMechanicalSqueezerFacade> STREAM_CODEC = StreamCodec.composite(\n            ByteBufCodecs.INT, RecipeMechanicalSqueezerFacade::getDuration,\n            RecipeMechanicalSqueezerFacade::new\n    );\n    public static final RecipeSerializer<RecipeMechanicalSqueezerFacade> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerMechanicalSqueezerFacadeConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n\n/**\n * Config for the drying Facade Squeeze Mechanical serializer.\n * @author kirjorjos\n *\n */\npublic class RecipeSerializerMechanicalSqueezerFacadeConfig extends RecipeConfigCommon<RecipeMechanicalSqueezerFacade, IntegratedDynamics> {\n\n    public RecipeSerializerMechanicalSqueezerFacadeConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_squeezer_facade\",\n                eConfig -> RecipeSerializerMechanicalSqueezerFacade.SERIALIZER\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerNbtClear.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\n\n/**\n * Recipe serializer for NBT clear recipes.\n * @author rubensworks\n */\npublic class RecipeSerializerNbtClear {\n\n    public static final MapCodec<RecipeNbtClear> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Ingredient.CODEC.fieldOf(\"item\").forGetter(RecipeNbtClear::getInputIngredient)\n                    )\n                    .apply(builder, RecipeNbtClear::new)\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeNbtClear> STREAM_CODEC = StreamCodec.composite(\n            Ingredient.CONTENTS_STREAM_CODEC, RecipeNbtClear::getInputIngredient,\n            RecipeNbtClear::new\n    );\n    public static final RecipeSerializer<RecipeNbtClear> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerNbtClearConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link RecipeNbtClear}.\n * @author rubensworks\n */\npublic class RecipeSerializerNbtClearConfig extends RecipeConfigCommon<RecipeNbtClear, IntegratedDynamics> {\n\n    public RecipeSerializerNbtClearConfig() {\n        super(IntegratedDynamics._instance,\n                \"crafting_special_nbt_clear\",\n                eConfig -> RecipeSerializerNbtClear.SERIALIZER);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.google.gson.JsonSyntaxException;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.cyclops.cyclopscore.codec.ListCodecStrict;\nimport org.cyclops.cyclopscore.helper.RecipeSerializerHelpers;\nimport org.cyclops.integrateddynamics.GeneralConfig;\n\nimport java.util.Optional;\n\n/**\n * Recipe serializer for squeezer recipes\n * @author rubensworks\n */\npublic class RecipeSerializerSqueezer {\n\n    public static final Codec<RecipeSqueezer.IngredientChance> CODEC_INGREDIENT_CHANCE = RecipeSerializerHelpers\n            .getCodecItemStackTemplateOrTagChance(() -> GeneralConfig.recipeTagOutputModPriorities)\n            .xmap(\n                    RecipeSqueezer.IngredientChance::new,\n                    RecipeSqueezer.IngredientChance::getIngredientChance\n            );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeSqueezer.IngredientChance> STREAM_CODEC_INGREDIENT_CHANCE = RecipeSerializerHelpers.STREAM_CODEC_ITEMSTACKTEMPLATE_OR_ITEMSTACKINGREDIENT_CHANCE\n            .map(\n                    RecipeSqueezer.IngredientChance::new,\n                    RecipeSqueezer.IngredientChance::getIngredientChance\n            );\n\n    public static final MapCodec<RecipeSqueezer> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> builder.group(\n                            Ingredient.CODEC.fieldOf(\"input_item\").forGetter(RecipeSqueezer::getInputIngredient),\n                            new ListCodecStrict<>(RecipeSerializerSqueezer.CODEC_INGREDIENT_CHANCE).optionalFieldOf(\"output_items\").forGetter(r -> r.getOutputItems().isEmpty() ? Optional.empty() : Optional.of(r.getOutputItems().stream().toList())),\n                            FluidStackTemplate.CODEC.optionalFieldOf(\"output_fluid\").forGetter(RecipeSqueezer::getOutputFluidTemplate)\n                    )\n                    .apply(builder, (inputIngredient, outputItemStacks, outputFluid) -> {\n                        // Validation\n                        if (outputItemStacks.isEmpty() && outputFluid.isEmpty()) {\n                            throw new JsonSyntaxException(\"An output item or fluid is required\");\n                        }\n\n                        return new RecipeSqueezer(inputIngredient, outputItemStacks.map(NonNullList::copyOf).orElseGet(NonNullList::create), outputFluid);\n                    })\n    );\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeSqueezer> STREAM_CODEC = StreamCodec.composite(\n            Ingredient.CONTENTS_STREAM_CODEC, RecipeSqueezer::getInputIngredient,\n            STREAM_CODEC_INGREDIENT_CHANCE.apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)), RecipeSqueezer::getOutputItems,\n            ByteBufCodecs.optional(FluidStackTemplate.STREAM_CODEC), RecipeSqueezer::getOutputFluidTemplate,\n            RecipeSqueezer::new\n    );\n    public static final RecipeSerializer<RecipeSqueezer> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the squeezer recipe serializer.\n * @author rubensworks\n *\n */\npublic class RecipeSerializerSqueezerConfig extends RecipeConfigCommon<RecipeSqueezer, IntegratedDynamics> {\n\n    public RecipeSerializerSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer\",\n                eConfig -> RecipeSerializerSqueezer.SERIALIZER\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerSqueezerFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\n\npublic class RecipeSerializerSqueezerFacade {\n\n    public static final MapCodec<RecipeSqueezerFacade> CODEC = RecordCodecBuilder.mapCodec(b -> b.point(RecipeSqueezerFacade.INSTANCE));\n    public static final StreamCodec<RegistryFriendlyByteBuf, RecipeSqueezerFacade> STREAM_CODEC = StreamCodec.unit(RecipeSqueezerFacade.INSTANCE);\n    public static final RecipeSerializer<RecipeSqueezerFacade> SERIALIZER = new RecipeSerializer<>(CODEC, STREAM_CODEC);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSerializerSqueezerFacadeConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n\n/**\n * Config for the Facade Squeeze recipe serializer.\n * @author kirjorjos\n *\n */\npublic class RecipeSerializerSqueezerFacadeConfig extends RecipeConfigCommon<RecipeSqueezerFacade, IntegratedDynamics> {\n\n    public RecipeSerializerSqueezerFacadeConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer_facade\",\n                eConfig -> RecipeSerializerSqueezerFacade.SERIALIZER\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.datafixers.util.Either;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.ItemStackTemplate;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.item.crafting.display.RecipeDisplay;\nimport net.minecraft.world.item.crafting.display.SlotDisplay;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.fluids.FluidStackTemplate;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.recipe.ItemStackFromIngredient;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplaySqueezer;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Squeezer recipe\n * @author rubensworks\n */\npublic class RecipeSqueezer implements Recipe<CraftingInput> {\n\n    private final Ingredient inputIngredient;\n    private final NonNullList<IngredientChance> outputItems;\n    private final Optional<FluidStackTemplate> outputFluid;\n\n    private PlacementInfo placementInfo;\n\n    public RecipeSqueezer(Ingredient inputIngredient,\n                          NonNullList<IngredientChance> outputItems,\n                          Optional<FluidStackTemplate> outputFluid) {\n        this.inputIngredient = inputIngredient;\n        this.outputItems = outputItems;\n        this.outputFluid = outputFluid;\n    }\n\n    public Ingredient getInputIngredient() {\n        return inputIngredient;\n    }\n\n    public NonNullList<IngredientChance> getOutputItems() {\n        return outputItems;\n    }\n\n    public List<Pair<? extends SlotDisplay, Float>> getOutputItemsAsSlots() {\n        return this.getOutputItems().stream().map(i -> i.ingredient.map(\n                left -> Pair.of(new SlotDisplay.ItemSlotDisplay(left.getLeft().item()), left.getRight()),\n                right -> Pair.of(right.getLeft().getIngredient().display(), right.getRight())\n        )).toList();\n    }\n\n    public Optional<FluidStackTemplate> getOutputFluidTemplate() {\n        return outputFluid;\n    }\n\n    public Optional<FluidStack> getOutputFluid() {\n        return outputFluid.map(FluidStackTemplate::create);\n    }\n\n    @Override\n    public boolean matches(CraftingInput inv, Level worldIn) {\n        return inputIngredient.test(inv.getItem(0));\n    }\n\n    @Override\n    public ItemStack assemble(CraftingInput inv) {\n        // Should not be called, but let's provide a good fallback\n        if (this.outputItems.isEmpty()) {\n            return ItemStack.EMPTY;\n        }\n        return this.outputItems.get(0).getIngredientFirst().copy();\n    }\n\n    @Override\n    public String group() {\n        return \"\";\n    }\n\n    @Override\n    public boolean showNotification() {\n        return false;\n    }\n\n    public NonNullList<IngredientChance> assemble(ItemStack inputItem) {\n        return getOutputItems();\n    }\n\n    @Override\n    public RecipeSerializer<? extends Recipe<CraftingInput>> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_SQUEEZER.get();\n    }\n\n    @Override\n    public RecipeType<? extends Recipe<CraftingInput>> getType() {\n        return RegistryEntries.RECIPETYPE_SQUEEZER.get();\n    }\n\n    @Override\n    public PlacementInfo placementInfo() {\n        if (this.placementInfo == null) {\n            this.placementInfo = PlacementInfo.create(this.inputIngredient);\n        }\n        return this.placementInfo;\n    }\n\n    @Override\n    public RecipeBookCategory recipeBookCategory() {\n        return RegistryEntries.RECIPEBOOKCATEGORY_SQUEEZER.get();\n    }\n\n    @Override\n    public List<RecipeDisplay> display() {\n        return List.of(new RecipeDisplaySqueezer(\n                this.getInputIngredient().display(),\n                this.getOutputItemsAsSlots(),\n                this.getOutputFluid().orElse(FluidStack.EMPTY),\n                new SlotDisplay.ItemSlotDisplay(RegistryEntries.BLOCK_SQUEEZER.get().asItem()),\n                0\n        ));\n    }\n\n    public static class IngredientChance {\n        private final Either<Pair<ItemStackTemplate, Float>, Pair<ItemStackFromIngredient, Float>> ingredient;\n\n        public IngredientChance(Either<Pair<ItemStackTemplate, Float>, Pair<ItemStackFromIngredient, Float>> ingredient) {\n            this.ingredient = Objects.requireNonNull(ingredient);\n        }\n\n        public Either<ItemStackTemplate, ItemStackFromIngredient> getIngredient() {\n            return ingredient.mapBoth(Pair::getLeft, Pair::getLeft);\n        }\n\n        public Either<Pair<ItemStackTemplate, Float>, Pair<ItemStackFromIngredient, Float>> getIngredientChance() {\n            return ingredient;\n        }\n\n        public ItemStack getIngredientFirst() {\n            return getIngredient().map(ItemStackTemplate::create, ItemStackFromIngredient::getFirstItemStack);\n        }\n\n        public float getChance() {\n            return ingredient.map(Pair::getRight, Pair::getRight);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeSqueezerFacade.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport com.mojang.datafixers.util.Either;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.ItemStackTemplate;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.Recipe;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.minecraft.world.level.Level;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.recipe.ItemStackFromIngredient;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.item.ItemFacade;\n\nimport java.util.Optional;\n\npublic class RecipeSqueezerFacade extends RecipeSqueezer {\n\n    public static IngredientChance OUTPUT = new IngredientChance(Either.left(Pair.of(new ItemStackTemplate(RegistryEntries.ITEM_FACADE), 1.0f)));\n\n    public static final RecipeSqueezerFacade INSTANCE = new RecipeSqueezerFacade();\n\n    private RecipeSqueezerFacade() {\n        super(Ingredient.of(RegistryEntries.ITEM_FACADE.get()), NonNullList.of(OUTPUT, OUTPUT), Optional.empty());\n    }\n\n    @Override\n    public NonNullList<IngredientChance> assemble(ItemStack inputItem) {\n        return getOutput(inputItem);\n    }\n\n    public static NonNullList<IngredientChance> getOutput(ItemStack inputItem) {\n        ItemFacade inputFacade = (ItemFacade)inputItem.getItem();\n        ItemStack facadeBlockItemStack = inputFacade.getFacadeBlockItem(inputItem);\n\n        if (facadeBlockItemStack == null) return NonNullList.of(OUTPUT, OUTPUT);\n        ItemStackTemplate facadeBlockItemTemplate = new ItemStackTemplate(facadeBlockItemStack.getItem(), facadeBlockItemStack.getComponentsPatch());\n        Either<ItemStackTemplate, ItemStackFromIngredient> inputItemEither = Either.left(facadeBlockItemTemplate);\n        IngredientChance outputItemChance = new IngredientChance(inputItemEither.mapBoth(i -> Pair.of(i, 1.0f), i -> Pair.of(i, 1.0f)));\n        return NonNullList.of(outputItemChance, outputItemChance, OUTPUT);\n    }\n\n    @Override\n    public boolean matches(CraftingInput inv, Level worldIn) {\n        return ((inv.getItem(0).getItem() instanceof ItemFacade) && (inv.getItem(0).has(RegistryEntries.DATACOMPONENT_FACADE_BLOCK)));\n    }\n\n    @Override\n    public RecipeSerializer<? extends Recipe<CraftingInput>> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_SQUEEZER_FACADE.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeTypeDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeTypeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the drying basin recipe type.\n * @author rubensworks\n *\n */\npublic class RecipeTypeDryingBasinConfig extends RecipeTypeConfigCommon<RecipeDryingBasin, IntegratedDynamics> {\n\n    public RecipeTypeDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"drying_basin\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeTypeMechanicalDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeTypeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the mechanical drying basin recipe type.\n * @author rubensworks\n *\n */\npublic class RecipeTypeMechanicalDryingBasinConfig extends RecipeTypeConfigCommon<RecipeMechanicalDryingBasin, IntegratedDynamics> {\n\n    public RecipeTypeMechanicalDryingBasinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_drying_basin\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeTypeMechanicalSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeTypeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the mechanical squeezer recipe type.\n * @author rubensworks\n *\n */\npublic class RecipeTypeMechanicalSqueezerConfig extends RecipeTypeConfigCommon<RecipeMechanicalSqueezer, IntegratedDynamics> {\n\n    public RecipeTypeMechanicalSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"mechanical_squeezer\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/recipe/type/RecipeTypeSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.core.recipe.type;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeTypeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the squeezer recipe type.\n * @author rubensworks\n *\n */\npublic class RecipeTypeSqueezerConfig extends RecipeTypeConfigCommon<RecipeSqueezer, IntegratedDynamics> {\n\n    public RecipeTypeSqueezerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"squeezer\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/test/IntegrationBefore.java",
    "content": "package org.cyclops.integrateddynamics.core.test;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for methods that need to be called before each integration test.\n * @author rubensworks\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface IntegrationBefore {\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/test/IntegrationTest.java",
    "content": "package org.cyclops.integrateddynamics.core.test;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for integration test methods..\n * @author rubensworks\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface IntegrationTest {\n    Class<?> expected() default Void.class;\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/core/test/TestHelpers.java",
    "content": "package org.cyclops.integrateddynamics.core.test;\n\nimport net.minecraft.core.HolderLookup;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.command.CommandTest;\nimport org.cyclops.integrateddynamics.gametest.integration.Asserts;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\n/**\n * Helpers for tests\n * @author rubensworks\n */\npublic class TestHelpers {\n\n    public static boolean canRunIntegrationTests() {\n        try {\n            Class.forName(CommandTest.CLASSES.get(0));\n            return true;\n        } catch (ClassNotFoundException e) {\n            return false;\n        }\n    }\n\n    /**\n     * Assertion for equal objects.\n     * @param actual Actual value.\n     * @param expected Expected value.\n     * @param ifEqual Message identifying the assertion.\n     * @param <T> The type.\n     */\n    public static <T> void assertEqual(T actual, T expected, String ifEqual) {\n        try {\n            if(actual instanceof Double) {\n                Asserts.check(((Double) actual - (Double) expected) < 0.0001D, ifEqual);\n            } else if(actual instanceof Float) {\n                Asserts.check(((Float) actual - (Float) expected) < 0.0001F, ifEqual);\n            } else {\n                Asserts.check(Objects.equals(actual, expected), ifEqual);\n            }\n        } catch (IllegalStateException e) {\n            throw new AssertionError(String.format(\"Failure: %s. Expected %s, but got %s.\", ifEqual, expected, actual));\n        }\n    }\n\n    /**\n     * Assertion for non equal objects.\n     * @param actual Actual value.\n     * @param expected Expected value.\n     * @param ifNonEqual Message identifying the assertion.\n     * @param <T> The type.\n     */\n    public static <T> void assertNonEqual(T actual, T expected, String ifNonEqual) {\n        try {\n            if(actual instanceof Double) {\n                Asserts.check(((Double) actual - (Double) expected) >= 0.0001D, ifNonEqual);\n            } else if(actual instanceof Float) {\n                Asserts.check(((Float) actual - (Float) expected) >= 0.0001F, ifNonEqual);\n            } else {\n                Asserts.check(!Objects.equals(actual, expected), ifNonEqual);\n            }\n        } catch (IllegalStateException e) {\n            throw new AssertionError(String.format(\"Failure: %s. Expected not %s, but got %s.\", ifNonEqual, expected, actual));\n        }\n    }\n\n    /**\n     * Assertion for null objects.\n     * @param actual Actual value.\n     * @param ifNull Message identifying the assertion.\n     * @param <T> The type.\n     */\n    public static <T> void assertNull(T actual, String ifNull) {\n        try {\n            Asserts.check(actual == null, ifNull);\n        } catch (IllegalStateException e) {\n            throw new AssertionError(String.format(\"Failure: %s. Expected to be null, but got %s.\", ifNull, actual));\n        }\n    }\n\n    /**\n     * Assertion for non null objects.\n     * @param actual Actual value.\n     * @param ifNonNull Message identifying the assertion.\n     * @param <T> The type.\n     */\n    public static <T> void assertNonNull(T actual, String ifNonNull) {\n        try {\n            Asserts.check(actual != null, ifNonNull);\n        } catch (IllegalStateException e) {\n            throw new AssertionError(String.format(\"Failure: %s. Expected to be non null, but got %s.\", ifNonNull, actual));\n        }\n    }\n\n    public static <T> CompoundTag serialize(Consumer<ValueOutput> deserializer) {\n        return serialize(deserializer, ValueDeseralizationContext.ofAllEnabled().holderLookupProvider());\n    }\n\n    public static <T> CompoundTag serialize(Consumer<ValueOutput> deserializer, HolderLookup.Provider holderLookup) {\n        TagValueOutput valueOutput = TagValueOutput.createWithContext(new ProblemReporter() {\n            @Override\n            public ProblemReporter forChild(PathElement p_421613_) {\n                return this;\n            }\n\n            @Override\n            public void report(Problem p_422137_) {\n\n            }\n        }, holderLookup);\n        deserializer.accept(valueOutput);\n        return valueOutput.buildResult();\n    }\n\n    public static <T> T deserialize(CompoundTag tag, Function<ValueInput, T> serializer) {\n        return deserialize(tag, serializer, ValueDeseralizationContext.ofAllEnabled().holderLookupProvider());\n    }\n\n    public static <T> T deserialize(CompoundTag tag, Function<ValueInput, T> serializer, HolderLookup.Provider holderLookup) {\n        ValueInput valueInput = TagValueInput.create(new ProblemReporter() {\n            @Override\n            public ProblemReporter forChild(PathElement p_421613_) {\n                return this;\n            }\n\n            @Override\n            public void report(Problem p_422137_) {\n\n            }\n        }, holderLookup, tag);\n        return serializer.apply(valueInput);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/entity/item/EntityItemTargetted.java",
    "content": "package org.cyclops.integrateddynamics.entity.item;\n\nimport net.minecraft.network.syncher.EntityDataAccessor;\nimport net.minecraft.network.syncher.EntityDataSerializers;\nimport net.minecraft.network.syncher.SynchedEntityData;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.util.Mth;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.client.particle.ParticleBlurData;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n\n/**\n * Entity item that can not despawn.\n * @author rubensworks\n *\n */\npublic class EntityItemTargetted extends ItemEntity {\n\n    private static final EntityDataAccessor<Float> TARGET_X = SynchedEntityData.defineId(EntityItemTargetted.class, EntityDataSerializers.FLOAT);\n    private static final EntityDataAccessor<Float> TARGET_Y = SynchedEntityData.defineId(EntityItemTargetted.class, EntityDataSerializers.FLOAT);\n    private static final EntityDataAccessor<Float> TARGET_Z = SynchedEntityData.defineId(EntityItemTargetted.class, EntityDataSerializers.FLOAT);\n\n    private LivingEntity targetEntity = null;\n\n    public EntityItemTargetted(EntityType<? extends EntityItemTargetted> entityType,  Level world) {\n        super(entityType, world);\n        this.lifespan = Integer.MAX_VALUE;\n    }\n\n    public EntityItemTargetted(Level p_32001_, double p_32002_, double p_32003_, double p_32004_, ItemStack p_32005_) {\n        this(p_32001_, p_32002_, p_32003_, p_32004_, p_32005_, p_32001_.getRandom().nextDouble() * 0.2 - 0.1, 0.2, p_32001_.getRandom().nextDouble() * 0.2 - 0.1);\n    }\n\n    public EntityItemTargetted(\n            Level p_149663_, double p_149664_, double p_149665_, double p_149666_, ItemStack p_149667_, double p_149668_, double p_149669_, double p_149670_\n    ) {\n        this(RegistryEntries.ENTITYTYPE_ITEM_TARGETTED.get(), p_149663_);\n        this.setPos(p_149664_, p_149665_, p_149666_);\n        this.setDeltaMovement(p_149668_, p_149669_, p_149670_);\n        this.setItem(p_149667_);\n        this.lifespan = (p_149667_.getItem() == null ? 6000 /*ItemEntity.LIFETIME*/ : p_149667_.getEntityLifespan(p_149663_));\n    }\n\n    public EntityItemTargetted(Level world, double x, double y, double z) {\n        this(world, x, y, z, ItemStack.EMPTY);\n        this.lifespan = Integer.MAX_VALUE;\n    }\n\n    @Override\n    protected void defineSynchedData(SynchedEntityData.Builder builder) {\n        super.defineSynchedData(builder);\n        builder.define(TARGET_X, (float) getY());\n        builder.define(TARGET_Y, (float) getY());\n        builder.define(TARGET_Z, (float) getY());\n    }\n\n    public void setTarget(float x, float y, float z) {\n        this.getEntityData().set(TARGET_X, x);\n        this.getEntityData().set(TARGET_Y, y);\n        this.getEntityData().set(TARGET_Z, z);\n    }\n\n    @Override\n    public boolean isNoGravity() {\n        return true;\n    }\n\n    public void setTarget(LivingEntity targetEntity) {\n        this.targetEntity = targetEntity;\n        this.setTarget((float) targetEntity.getX(), (float) targetEntity.getY(), (float) targetEntity.getZ());\n    }\n\n    public float getTargetX() {\n        return this.getEntityData().get(TARGET_X);\n    }\n\n    public float getTargetY() {\n        return this.getEntityData().get(TARGET_Y);\n    }\n\n    public float getTargetZ() {\n        return this.getEntityData().get(TARGET_Z);\n    }\n\n    @Override\n    public void tick() {\n        super.tick();\n\n        if (targetEntity != null) {\n            this.setTarget((float) targetEntity.getX(), (float) targetEntity.getY(), (float) targetEntity.getZ());\n        }\n\n        double dx = this.getX() - getTargetX();\n        double dy = this.getY() - (getTargetY() + 1F);\n        double dz = this.getZ() - getTargetZ();\n        double strength = -0.1;\n\n        double d = Mth.sqrt((float) (dx * dx + dy * dy + dz * dz));\n        if(d > 1D) {\n            double m = (1 / (2 * (Math.max(1, d)))) * strength;\n            dx *= m;\n            dy *= m;\n            dz *= m;\n            this.setDeltaMovement(dx, dy, dz);\n            if(this.horizontalCollision) {\n                this.setDeltaMovement(this.getDeltaMovement().x, 0.3, this.getDeltaMovement().z);\n            }\n        }\n        if (!level().isClientSide() && random.nextInt(5) == 0) {\n            showEntityMoved();\n        }\n    }\n\n    protected void showEntityMoved() {\n        RandomSource rand = level().getRandom();\n        float scale = 0.10F;\n        float red = rand.nextFloat() * 0.20F + 0.8F;\n        float green = rand.nextFloat() * 0.20F + 0.8F;\n        float blue = rand.nextFloat() * 0.10F + 0.10F;\n        float ageMultiplier = (float) (rand.nextDouble() * 25D + 50D);\n\n        ((ServerLevel) level()).sendParticles(\n                new ParticleBlurData(red, green, blue, scale, ageMultiplier),\n                this.getX(), this.getY() + 0.5D, this.getZ(), 1,\n                0.1 - rand.nextFloat() * 0.2, 0.1 - rand.nextFloat() * 0.2, 0.1 - rand.nextFloat() * 0.2, 0D);\n\n        if (rand.nextInt(5) == 0) {\n            double dx = this.getX() - (getTargetX() + 0.5F);\n            double dy = this.getY() - (getTargetY() + 1F);\n            double dz = this.getZ() - (getTargetZ() + 0.5F);\n            double factor = rand.nextDouble();\n            double x = this.getX() - dx * factor;\n            double y = this.getY() - dy * factor;\n            double z = this.getZ() - dz * factor;\n            ((ServerLevel) level()).sendParticles(\n                    new ParticleBlurData(red, green, blue, scale, ageMultiplier),\n                    x, y, z, 1,\n                    -0.02 * dx, -0.02 * dy, -0.02 * dz, 0D);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/entity/item/EntityItemTargettedConfig.java",
    "content": "package org.cyclops.integrateddynamics.entity.item;\n\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.MobCategory;\nimport org.cyclops.cyclopscore.config.extendedconfig.EntityClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.EntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the {@link EntityItemTargetted}.\n * @author rubensworks\n *\n */\npublic class EntityItemTargettedConfig extends EntityConfigCommon<IntegratedDynamics, EntityItemTargetted> {\n\n    public EntityItemTargettedConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"item_targetted\",\n                eConfig -> EntityType.Builder.<EntityItemTargetted>of(EntityItemTargetted::new, MobCategory.MISC)\n                        .fireImmune()\n                        .setShouldReceiveVelocityUpdates(true)\n                        .sized(0.25F, 0.25F)\n        );\n    }\n\n    @Override\n    public EntityClientConfig<IntegratedDynamics, EntityItemTargetted> constructEntityClientConfig() {\n        return new EntityItemTargettedConfigClient(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/entity/item/EntityItemTargettedConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.entity.item;\n\nimport net.minecraft.client.renderer.entity.EntityRenderer;\nimport net.minecraft.client.renderer.entity.EntityRendererProvider;\nimport org.cyclops.cyclopscore.config.extendedconfig.EntityClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.EntityConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class EntityItemTargettedConfigClient extends EntityClientConfig<IntegratedDynamics, EntityItemTargetted> {\n    public EntityItemTargettedConfigClient(EntityConfigCommon<IntegratedDynamics, EntityItemTargetted> entityConfig) {\n        super(entityConfig);\n    }\n\n    @Override\n    public EntityRenderer<? super EntityItemTargetted, ?> getRender(EntityRendererProvider.Context renderContext) {\n        return new net.minecraft.client.renderer.entity.ItemEntityRenderer(renderContext);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/fluid/FluidLiquidChorusConfig.java",
    "content": "package org.cyclops.integrateddynamics.fluid;\n\nimport net.minecraft.sounds.SoundEvents;\nimport net.minecraft.world.item.Rarity;\nimport net.neoforged.neoforge.common.SoundActions;\nimport org.cyclops.cyclopscore.config.extendedconfig.FluidConfigNeoForge;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for Liquid Chorus.\n * @author rubensworks\n *\n */\npublic class FluidLiquidChorusConfig extends FluidConfigNeoForge {\n\n    public FluidLiquidChorusConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"liquid_chorus\",\n                fluidConfig -> getDefaultFluidProperties(fluidConfig, builder -> builder\n                                .density(1500)\n                                .viscosity(3000)\n                                .rarity(Rarity.EPIC)\n                                .supportsBoating(true)\n                                .canHydrate(true)\n                                .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL)\n                                .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY)\n                                .descriptionId(\"block.integrateddynamics.block_liquid_chorus\"))\n                        .bucket(RegistryEntries.ITEM_BUCKET_LIQUID_CHORUS::get)\n                        .block(RegistryEntries.BLOCK_FLUID_LIQUID_CHORUS::get)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/fluid/FluidMenrilResinConfig.java",
    "content": "package org.cyclops.integrateddynamics.fluid;\n\nimport net.minecraft.sounds.SoundEvents;\nimport net.minecraft.world.item.Rarity;\nimport net.neoforged.neoforge.common.SoundActions;\nimport org.cyclops.cyclopscore.config.extendedconfig.FluidConfigNeoForge;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for Menril Resin.\n * @author rubensworks\n *\n */\npublic class FluidMenrilResinConfig extends FluidConfigNeoForge {\n\n    public FluidMenrilResinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_resin\",\n                fluidConfig -> getDefaultFluidProperties(fluidConfig, builder -> builder\n                                .density(1500)\n                                .viscosity(3000)\n                                .rarity(Rarity.RARE)\n                                .supportsBoating(true)\n                                .canHydrate(true)\n                                .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL)\n                                .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY)\n                                .descriptionId(\"block.integrateddynamics.block_menril_resin\"))\n                        .bucket(RegistryEntries.ITEM_BUCKET_MENRIL_RESIN::get)\n                        .block(RegistryEntries.BLOCK_FLUID_MENRIL_RESIN::get)\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestHelpersIntegratedDynamics.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestAssertException;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IAspectVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.Variable;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.item.AspectVariableFacade;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.OperatorLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\n/**\n * @author rubensworks\n */\npublic class GameTestHelpersIntegratedDynamics {\n\n    public static void assertValueEqual(GameTestHelper helper, IValue value1, IValue value2) {\n        if (!Objects.equals(value1, value2)) {\n            throw new GameTestAssertException(Component.literal(String.format(\"Value %s does not equal value %s\", value1, value2)), (int) helper.getTick());\n        }\n    }\n\n    public static void assertValueEqual(GameTestHelper helper, @Nullable IVariable value1, IValue value2) {\n        if (value1 == null) {\n            throw new GameTestAssertException(Component.literal(\"Variable is null\"), (int) helper.getTick());\n        }\n        try {\n            assertValueEqual(helper, value1.getValue(), value2);\n        } catch (EvaluationException e) {\n            throw new GameTestAssertException(e.getErrorMessage(), (int) helper.getTick());\n        }\n    }\n\n    public static ItemStack createVariableForValue(Level level, IValueType valueType, IValue value) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_VARIABLE);\n        return registry.writeVariableFacadeItem(true, itemStack, ValueTypes.REGISTRY,\n                new ValueTypeLPElementBase.ValueTypeVariableFacadeFactory(valueType, value), level, null, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get().defaultBlockState());\n    }\n\n    public static ItemStack createVariableForOperator(Level level, IOperator operator, int[] variableIds) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_VARIABLE);\n        return registry.writeVariableFacadeItem(true, itemStack, Operators.REGISTRY,\n                new OperatorLPElement.OperatorVariableFacadeFactory(operator, variableIds), level, null, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get().defaultBlockState());\n    }\n\n    public static IVariableFacade getVariableFacade(Level level, ItemStack itemStack) {\n        return RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(ValueDeseralizationContext.of(level), itemStack);\n    }\n\n    public static ItemStack createVariableFromReader(Level level, PartPos partPos, final IAspect aspect) {\n        return createVariableFromReader(level, aspect, PartHelpers.getPart(partPos).getState());\n    }\n\n    public static ItemStack createVariableFromReader(Level level, final IAspect aspect, IPartState partState) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_VARIABLE);\n        return registry.writeVariableFacadeItem(true, itemStack, Aspects.REGISTRY, new IVariableFacadeHandlerRegistry.IVariableFacadeFactory<>() {\n            @Override\n            public IAspectVariableFacade create(boolean generateId) {\n                return new AspectVariableFacade(generateId, partState.getId(), aspect);\n            }\n\n            @Override\n            public IAspectVariableFacade create(int id) {\n                return new AspectVariableFacade(id, partState.getId(), aspect);\n            }\n        }, level, null, null);\n    }\n\n    public static void placeVariableInWriter(GameTestHelper helper, Level level, PartPos partPos, final IAspectWrite<?, ?> aspect, ItemStack variableAspect) {\n        PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(partPos);\n        IPartTypeWriter<?, ?> part = (IPartTypeWriter<?, ?>) partStateHolder.getPart();\n        IPartStateWriter<?> state = (IPartStateWriter<?>) partStateHolder.getState();\n\n        // Find aspect index\n        int aspectIndex = -1;\n        for (int i = 0; i < part.getWriteAspects().size(); i++) {\n            if (part.getWriteAspects().get(i) == aspect) {\n                aspectIndex = i;\n            }\n        }\n        if (aspectIndex < 0) {\n            throw new GameTestAssertException(Component.literal(\"Aspect \" + aspect + \" not found\"), (int) helper.getTick());\n        }\n\n        // Insert variable\n        state.getInventory().setItem(aspectIndex, variableAspect);\n\n        // Activate aspect\n        ((IPartTypeWriter) part).updateActivation(PartTarget.fromCenter(partPos), state, null, false);\n    }\n\n    public static Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> placeVariableInDisplayPanel(Level level, PartPos partPos, ItemStack variableAspect) {\n        PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(partPos);\n        PartTypePanelDisplay part = (PartTypePanelDisplay) partStateHolder.getPart();\n        PartTypePanelDisplay.State state = (PartTypePanelDisplay.State) partStateHolder.getState();\n\n        // Insert variable\n        state.getInventory().setItem(0, variableAspect);\n\n        return Pair.of(part, state);\n    }\n\n    public static Supplier<IAspectVariable> testReadAspectSetup(BlockPos pos, GameTestHelper helper, IPartTypeReader<?, ?> partType, IAspectRead<?, ?> aspectRead) {\n        // Place cable\n        helper.setBlock(pos, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place part\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(pos), Direction.WEST, partType, new ItemStack(partType.getItem()));\n        PartPos partPos = PartPos.of(helper.getLevel(), helper.absolutePos(pos), Direction.WEST);\n        PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(partPos);\n        IPartTypeReader partReader = (IPartTypeReader) partStateHolder.getPart();\n        IPartStateReader partStateReader = (IPartStateReader) partStateHolder.getState();\n        return () -> partReader.getVariable(PartTarget.fromCenter(partPos), partStateReader, aspectRead);\n    }\n\n    public static <V extends IValue> void testReadAspectThrows(BlockPos pos, GameTestHelper helper, IPartTypeReader<?, ?> partType, IAspectRead<V, ?> aspectRead) {\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(pos, helper, partType, aspectRead);\n        helper.succeedWhen(() -> {\n            try {\n                variableSupplier.get().getValue();\n                helper.assertTrue(false, Component.literal(\"The aspect did not throw\"));\n            } catch (EvaluationException e) {\n                helper.assertTrue(true, Component.literal(\"The aspect did successfully throw\"));\n            }\n        });\n    }\n\n    public static <V extends IValue> void testReadAspect(BlockPos pos, GameTestHelper helper, IPartTypeReader<?, ?> partType, IAspectRead<V, ?> aspectRead, V expectedValue) {\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(pos, helper, partType, aspectRead);\n        helper.succeedWhen(() -> assertValueEqual(helper, variableSupplier.get(), expectedValue));\n    }\n\n    public static <V extends IValue> void testReadAspectPredicate(BlockPos pos, GameTestHelper helper, IPartTypeReader<?, ?> partType, IAspectRead<V, ?> aspectRead, Predicate<V> asserter) {\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(pos, helper, partType, aspectRead);\n        helper.succeedWhen(() -> {\n            try {\n                helper.assertTrue(asserter.test((V) variableSupplier.get().getValue()), Component.literal(\"Value was not asserted correctly\"));\n            } catch (EvaluationException e) {\n                throw new RuntimeException(e);\n            }\n        });\n    }\n\n    public static <V extends IValue> void testReadAspectOperator(BlockPos pos, GameTestHelper helper, IPartTypeReader<?, ?> partType, IAspectRead<V, ?> aspectRead, List<IValue> operatorInputs, IValue expectedOperatorOutput) {\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(pos, helper, partType, aspectRead);\n        helper.succeedWhen(() -> {\n            try {\n                IValue operatorValue = variableSupplier.get().getValue();\n                if (!(operatorValue instanceof ValueTypeOperator.ValueOperator operator)) {\n                    throw new GameTestAssertException(Component.literal(\"Return value is not an operator\"), (int) helper.getTick());\n                }\n                IValue output = operator.getRawValue().evaluate(operatorInputs.stream().map(Variable::new).toArray(Variable[]::new));\n                assertValueEqual(helper, output, expectedOperatorOutput);\n            } catch (EvaluationException e) {\n                throw new GameTestAssertException(e.getErrorMessage(), (int) helper.getTick());\n            }\n        });\n    }\n\n    public static <V extends IValue> void testReadAspectOperatorPredicate(BlockPos pos, GameTestHelper helper, IPartTypeReader<?, ?> partType, IAspectRead<V, ?> aspectRead, List<IValue> operatorInputs, Predicate<IValue> asserter) {\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(pos, helper, partType, aspectRead);\n        helper.succeedWhen(() -> {\n            try {\n                IValue operatorValue = variableSupplier.get().getValue();\n                if (!(operatorValue instanceof ValueTypeOperator.ValueOperator operator)) {\n                    throw new GameTestAssertException(Component.literal(\"Return value is not an operator\"), (int) helper.getTick());\n                }\n                IValue output = operator.getRawValue().evaluate(operatorInputs.stream().map(Variable::new).toArray(Variable[]::new));\n                helper.assertTrue(asserter.test(output), Component.literal(\"Value was not asserted correctly\"));\n            } catch (EvaluationException e) {\n                throw new GameTestAssertException(e.getErrorMessage(), (int) helper.getTick());\n            }\n        });\n    }\n\n    public static <V extends IValue> void testWriteAspectSetup(BlockPos pos, GameTestHelper helper, IPartTypeWriter<?, ?> partType, IAspectWrite<V, ?> aspectWrite, ItemStack variableAspect) {\n        // Place cable\n        if (helper.getLevel().isEmptyBlock(helper.absolutePos(pos))) {\n            helper.setBlock(pos, RegistryEntries.BLOCK_CABLE.value());\n        }\n\n        // Place part\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(pos), Direction.WEST, partType, new ItemStack(partType.getItem()));\n        PartPos partPos = PartPos.of(helper.getLevel(), helper.absolutePos(pos), Direction.WEST);\n        placeVariableInWriter(helper, helper.getLevel(), partPos, aspectWrite, variableAspect);\n    }\n\n    public static <V extends IValue> void testWriteAspectSetup(BlockPos pos, GameTestHelper helper, IPartTypeWriter<?, ?> partType, IAspectWrite<V, ?> aspectWrite, V value) {\n        testWriteAspectSetup(pos, helper, partType, aspectWrite, createVariableForValue(helper.getLevel(), value.getType(), value));\n    }\n\n    public static <T extends IValueType<V>, V extends IValue> void setAspectProperty(PartPos partPos, IAspect aspect, IAspectPropertyTypeInstance<T, V> property, V value) {\n        PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(partPos);\n        IPartState<?> state = partStateHolder.getState();\n\n        // Get or create aspect properties\n        IAspectProperties properties = state.getAspectProperties(aspect);\n        if (properties == null) {\n            properties = aspect.getDefaultProperties();\n        }\n\n        // Set the property value\n        properties.setValue(property, value);\n        state.setAspectProperties(aspect, properties);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAdvancements.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.advancements.AdvancementHolder;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.gametest.framework.GameTestAssertException;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Blocks;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.event.entity.player.PlayerContainerEvent;\nimport net.neoforged.neoforge.event.entity.player.PlayerEvent;\nimport org.cyclops.commoncapabilities.IngredientComponents;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.expression.ILazyExpressionValueCache;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariableInvalidateListener;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.expression.LazyExpression;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.event.LogicProgrammerVariableFacadeCreatedEvent;\nimport org.cyclops.integrateddynamics.core.network.event.NetworkInitializedEvent;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.core.part.event.PartReaderAspectEvent;\nimport org.cyclops.integrateddynamics.core.part.event.PartVariableDrivenVariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammer;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\n/**\n * Game tests for all advancements in IntegratedDynamics.\n * @author rubensworks\n */\npublic class GameTestsAdvancements {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    // ---- Helpers ----\n\n    @SuppressWarnings(\"removal\")\n    private static ServerPlayer mockServerPlayer(GameTestHelper helper) {\n        return helper.makeMockServerPlayerInLevel();\n    }\n\n    private static void assertAdvancement(GameTestHelper helper, ServerPlayer player, String id) {\n        AdvancementHolder advancement = helper.getLevel().getServer().getAdvancements().get(Identifier.parse(id));\n        if (advancement == null) {\n            throw new GameTestAssertException(Component.literal(\"Advancement not found: \" + id), (int) helper.getTick());\n        }\n        if (!player.getAdvancements().getOrStartProgress(advancement).isDone()) {\n            throw new GameTestAssertException(Component.literal(\"Advancement not obtained: \" + id), (int) helper.getTick());\n        }\n    }\n\n    private static void assertAdvancementNotDone(GameTestHelper helper, ServerPlayer player, String id) {\n        AdvancementHolder advancement = helper.getLevel().getServer().getAdvancements().get(Identifier.parse(id));\n        if (advancement != null && player.getAdvancements().getOrStartProgress(advancement).isDone()) {\n            throw new GameTestAssertException(Component.literal(\"Advancement should NOT have been obtained: \" + id), (int) helper.getTick());\n        }\n    }\n\n    private static ILazyExpressionValueCache simpleCache() {\n        return new ILazyExpressionValueCache() {\n            private final Map<Integer, IValue> values = new HashMap<>();\n            @Override public void setValue(int id, IValue value) { values.put(id, value); }\n            @Override public boolean hasValue(int id) { return values.containsKey(id); }\n            @Override public IValue getValue(int id) { return values.get(id); }\n            @Override public void removeValue(int id) { values.remove(id); }\n        };\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private static <V extends IValue> LazyExpression<V> makeOpVar(IOperator operator, IValueType<V> type, IVariable<?>... inputs) {\n        ILazyExpressionValueCache cache = simpleCache();\n        return new LazyExpression<V>(0, operator, inputs, cache) {\n            @Override public IValueType<V> getType() { return type; }\n            @Override public V getValue() throws EvaluationException { return type.getDefault(); }\n        };\n    }\n\n    private static <V extends IValue> IAspectVariable<V> makeAspectVar(IAspectRead<V, ?> aspect) {\n        return new IAspectVariable<V>() {\n            @Override public PartTarget getTarget() { return null; }\n            @Override public IAspectRead<V, ?> getAspect() { return aspect; }\n            @Override public IValueType<V> getType() { return aspect.getValueType(); }\n            @Override public V getValue() throws EvaluationException { return aspect.getValueType().getDefault(); }\n            @Override public void invalidate() {}\n            @Override public void addInvalidationListener(IVariableInvalidateListener l) {}\n            @Override public void removeInvalidationListener(IVariableInvalidateListener l) {}\n        };\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private static void fireVariableDrivenEvent(ServerPlayer player, IVariable<?> variable) {\n        NeoForge.EVENT_BUS.post(new PartVariableDrivenVariableContentsUpdatedEvent(\n                null, null, null, PartTypes.DISPLAY_PANEL, null, player, variable, null));\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private static void fireReaderAspectEvent(GameTestHelper helper, ServerPlayer player, BlockPos pos,\n            org.cyclops.integrateddynamics.api.part.read.IPartTypeReader<?, ?> partType, IAspectRead<?, ?> aspect) {\n        helper.setBlock(pos, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(pos), Direction.WEST,\n                partType, new ItemStack(partType.getItem()));\n        PartPos partPos = PartPos.of(helper.getLevel(), helper.absolutePos(pos), Direction.WEST);\n        PartHelpers.PartStateHolder<?, ?> holder = PartHelpers.getPart(partPos);\n        INetwork network = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(pos), null);\n        org.cyclops.integrateddynamics.api.network.IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n        PartTarget target = PartTarget.fromCenter(partPos);\n        NeoForge.EVENT_BUS.post(new PartReaderAspectEvent(\n                network, partNetwork, target, partType,\n                (org.cyclops.integrateddynamics.api.part.read.IPartStateReader) holder.getState(),\n                player, aspect, ItemStack.EMPTY));\n    }\n\n    private static void placeVariableInWriterWithPlayer(Level level, PartPos partPos,\n            IAspectWrite<?, ?> aspect, ItemStack variableCard, ServerPlayer player) {\n        PartHelpers.PartStateHolder<?, ?> holder = PartHelpers.getPart(partPos);\n        IPartTypeWriter<?, ?> part = (IPartTypeWriter<?, ?>) holder.getPart();\n        IPartStateWriter<?> state = (IPartStateWriter<?>) holder.getState();\n\n        int aspectIndex = -1;\n        for (int i = 0; i < part.getWriteAspects().size(); i++) {\n            if (part.getWriteAspects().get(i) == aspect) {\n                aspectIndex = i;\n                break;\n            }\n        }\n        if (aspectIndex < 0) {\n            throw new GameTestAssertException(Component.literal(\"Aspect \" + aspect + \" not found in writer\"), 0);\n        }\n\n        state.getInventory().setItem(aspectIndex, variableCard);\n        ((IPartTypeWriter) part).updateActivation(PartTarget.fromCenter(partPos), state, player, false);\n    }\n\n    // ---- Tests ----\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRoot(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack anyIDItem = new ItemStack(RegistryEntries.ITEM_VARIABLE.get());\n        NeoForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, anyIDItem, null));\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:root\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMeneglinDiscovery(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack menrilLog = new ItemStack(RegistryEntries.BLOCK_MENRIL_LOG.get());\n        player.addItem(menrilLog);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:meneglin_basics/meneglin_discovery\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMenrilProduction(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack crystalBlock = new ItemStack(RegistryEntries.BLOCK_CRYSTALIZED_MENRIL_BLOCK.get());\n        player.addItem(crystalBlock);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:meneglin_basics/menril_production\"));\n    }\n\n    private static void fireItemCraftedTrigger(ServerPlayer player, String itemId) {\n        ItemStack stack = new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId)));\n        NeoForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, stack, null));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementSqueezing(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:squeezer\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:meneglin_basics/squeezing\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementDrying(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:drying_basin\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:meneglin_basics/drying\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementCablesLogic(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:cable\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:network_foundations/cables_logic\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMenrilWrenching(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:wrench\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:network_foundations/menril_wrenching\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariables(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:variable\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:network_foundations/variables\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableInput(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:variable_transformer_input\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:network_foundations/variable_input\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableOutput(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:variable_transformer_output\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:network_foundations/variable_output\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementBlockReading(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_block_reader\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:basic_network_components/block_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementInventoryReading(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_inventory_reader\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:basic_network_components/inventory_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMachineReading(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_machine_reader\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:basic_network_components/machine_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneReading(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_redstone_reader\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:basic_network_components/redstone_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementValueDisplaying(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_display_panel\");\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:basic_network_components/value_displaying\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLogicProgramming(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ContainerLogicProgrammer container = new ContainerLogicProgrammer(0, player.getInventory());\n        NeoForge.EVENT_BUS.post(new PlayerContainerEvent.Open(player, container));\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:logic_operations/logic_programming\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementNetworksLogic(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Place 10 connected cables\n        for (int i = 0; i < 10; i++) {\n            helper.setBlock(POS.east(i), RegistryEntries.BLOCK_CABLE.value());\n        }\n        INetwork network = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n        NeoForge.EVENT_BUS.post(new NetworkInitializedEvent(\n                network, helper.getLevel(), helper.absolutePos(POS), player));\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:network_foundations/networks_logic\"));\n    }\n\n    private static void fireVariableCreatedTrigger(ServerPlayer player, IVariableFacade facade,\n            net.minecraft.world.level.block.Block block) {\n        NeoForge.EVENT_BUS.post(new LogicProgrammerVariableFacadeCreatedEvent(\n                player, facade, block.defaultBlockState()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementConstantDefinition(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get());\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:logic_operations/constant_definition\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableMaterialization(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_MATERIALIZER.get());\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:advanced_network_components/variable_materialization\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableProxying(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_PROXY.get());\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:advanced_network_components/variable_proxying\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecipeCreation(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.OBJECT_RECIPE,\n                ValueObjectTypeRecipe.ValueRecipe.of(null));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get());\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:recipe_handling/recipe_creation\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneCapturing(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireReaderAspectEvent(helper, player, POS, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.INTEGER_VALUE);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:aspects/redstone_capturing\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecipeReading(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        fireReaderAspectEvent(helper, player, POS, PartTypes.MACHINE_READER, Aspects.Read.Machine.LIST_GETRECIPES);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:recipe_handling/recipe_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneObservment(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        IAspectVariable<ValueTypeInteger.ValueInteger> aspectVar =\n                makeAspectVar(Aspects.Read.Redstone.INTEGER_VALUE);\n        fireVariableDrivenEvent(player, aspectVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:aspects/redstone_observement\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementArithmeticAddition(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        LazyExpression<ValueTypeInteger.ValueInteger> opVar =\n                makeOpVar(Operators.ARITHMETIC_ADDITION, ValueTypes.INTEGER);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:logic_operations/arithmetic_addition\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementItemOriginIdentification(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        LazyExpression<ValueTypeString.ValueString> opVar =\n                makeOpVar(Operators.OBJECT_ITEMSTACK_MODNAME, ValueTypes.STRING);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:advanced_operations/item_origin_identification\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLogicalListBuilding(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        ValueTypeList.ValueList listValue = ValueTypeList.ValueList.ofList(ValueTypes.INTEGER,\n                Arrays.asList(\n                        ValueTypeInteger.ValueInteger.of(1),\n                        ValueTypeInteger.ValueInteger.of(10),\n                        ValueTypeInteger.ValueInteger.of(100)));\n        Variable<ValueTypeList.ValueList> listVar = new Variable<>(ValueTypes.LIST, listValue);\n        fireVariableDrivenEvent(player, listVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:advanced_operations/logical_list_building\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementWhatWouldIBeLookingAt(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        LazyExpression<ValueObjectTypeBlock.ValueBlock> opVar =\n                makeOpVar(Operators.OBJECT_PLAYER_TARGETBLOCK, ValueTypes.OBJECT_BLOCK);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecipeLookup(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // input[0]: aspect variable for recipe-by-output aspect\n        IAspectVariable<ValueTypeOperator.ValueOperator> input0 =\n                makeAspectVar(Aspects.Read.Machine.OPERATOR_GETRECIPEBYOUTPUT);\n        // input[1]: ingredients value (chest x1)\n        ValueObjectTypeIngredients.ValueIngredients ingredientsValue =\n                ValueObjectTypeIngredients.ValueIngredients.of(\n                        MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK,\n                                new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:chest\")), 1)));\n        Variable<ValueObjectTypeIngredients.ValueIngredients> input1 =\n                new Variable<>(ValueTypes.OBJECT_INGREDIENTS, ingredientsValue);\n        LazyExpression<ValueTypeOperator.ValueOperator> opVar =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.OPERATOR, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:recipe_handling/recipe_lookup\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLecternData(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        IAspectVariable<ValueTypeNbt.ValueNbt> aspectVar = makeAspectVar(Aspects.Read.Block.NBT);\n        fireVariableDrivenEvent(player, aspectVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:nbt/lectern_data\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLecternBook(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // input[0]: aspect variable for read_nbt_block_tile\n        IAspectVariable<ValueTypeNbt.ValueNbt> input0 = makeAspectVar(Aspects.Read.Block.NBT);\n        // input[1]: string value \"Book\"\n        Variable<ValueTypeString.ValueString> input1 =\n                new Variable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"Book\"));\n        LazyExpression<ValueTypeNbt.ValueNbt> opVar =\n                makeOpVar(Operators.NBT_COMPOUND_VALUE_COMPOUND, ValueTypes.NBT, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:nbt/lectern_book\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLecternBookName(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // input[0]: string value \".Book.id\"\n        Variable<ValueTypeString.ValueString> input0 =\n                new Variable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\".Book.id\"));\n        // input[1]: aspect variable for read_nbt_block_tile\n        IAspectVariable<ValueTypeNbt.ValueNbt> input1 = makeAspectVar(Aspects.Read.Block.NBT);\n        LazyExpression<ValueTypeNbt.ValueNbt> opVar =\n                makeOpVar(Operators.NBT_PATH_MATCH_FIRST, ValueTypes.NBT, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:nbt/lectern_book_name\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementDynamicAdditions(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // input[0] of outer OPERATOR_APPLY: inner LazyExpression(OPERATOR_APPLY) with type OPERATOR\n        LazyExpression<ValueTypeOperator.ValueOperator> inner =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.OPERATOR);\n        // input[1] of outer: any integer variable\n        Variable<ValueTypeInteger.ValueInteger> intVar =\n                new Variable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        LazyExpression<ValueTypeInteger.ValueInteger> opVar =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.INTEGER, inner, intVar);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:the_value_of_operators/dynamic_additions\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementDynamicListFiltering(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // The value predicate checks that the result is a list [10]\n        ValueTypeList.ValueList resultList = ValueTypeList.ValueList.ofList(ValueTypes.INTEGER,\n                Arrays.asList(ValueTypeInteger.ValueInteger.of(10)));\n        ILazyExpressionValueCache cache = simpleCache();\n        LazyExpression<ValueTypeList.ValueList> opVar =\n                new LazyExpression<ValueTypeList.ValueList>(0, Operators.OPERATOR_FILTER, new IVariable[0], cache) {\n                    @Override public IValueType<ValueTypeList.ValueList> getType() { return ValueTypes.LIST; }\n                    @Override public ValueTypeList.ValueList getValue() { return resultList; }\n                };\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:the_value_of_operators/dynamic_list_filtering\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementCreeperTaming(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Spawn a creeper as the input entity\n        net.minecraft.world.entity.Entity creeper = helper.spawn(EntityType.CREEPER, POS.above());\n        Variable<ValueObjectTypeEntity.ValueEntity> creeperVar = new Variable<>(\n                ValueTypes.OBJECT_ENTITY, ValueObjectTypeEntity.ValueEntity.of(creeper));\n        LazyExpression<ValueTypeDouble.ValueDouble> opVar =\n                makeOpVar(Operators.OBJECT_ENTITY_HEALTH, ValueTypes.DOUBLE, creeperVar);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:challenges/creeper_taming\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecursiveRecursion(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // An infinite list satisfies the \"infinite_list: true\" predicate\n        ValueTypeList.ValueList infiniteList = ValueTypeList.ValueList.ofFactory(\n                new ValueTypeListProxyLazyBuilt<>(ValueTypeInteger.ValueInteger.of(0), Operators.ARITHMETIC_ADDITION));\n        Variable<ValueTypeList.ValueList> listVar = new Variable<>(ValueTypes.LIST, infiniteList);\n        fireVariableDrivenEvent(player, listVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:challenges/recursive_recursion\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementToolForObsidian(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // input[0]: stone_pickaxe itemstack value\n        Variable<ValueObjectTypeItemStack.ValueItemStack> input0 = new Variable<>(\n                ValueTypes.OBJECT_ITEMSTACK,\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(\n                        BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:stone_pickaxe\")))));\n        // input[1]: obsidian block value\n        Variable<ValueObjectTypeBlock.ValueBlock> input1 = new Variable<>(\n                ValueTypes.OBJECT_BLOCK,\n                ValueObjectTypeBlock.ValueBlock.of(Blocks.OBSIDIAN.defaultBlockState()));\n        LazyExpression<ValueTypeBoolean.ValueBoolean> opVar =\n                makeOpVar(Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK, ValueTypes.BOOLEAN, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:challenges/tool_for_obsidian\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementToolForObsidianNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // input[0]: bone_meal itemstack value (NOT stone_pickaxe - should NOT trigger the advancement)\n        Variable<ValueObjectTypeItemStack.ValueItemStack> input0 = new Variable<>(\n                ValueTypes.OBJECT_ITEMSTACK,\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(\n                        BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:bone_meal\")))));\n        // input[1]: obsidian block value\n        Variable<ValueObjectTypeBlock.ValueBlock> input1 = new Variable<>(\n                ValueTypes.OBJECT_BLOCK,\n                ValueObjectTypeBlock.ValueBlock.of(Blocks.OBSIDIAN.defaultBlockState()));\n        LazyExpression<ValueTypeBoolean.ValueBoolean> opVar =\n                makeOpVar(Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK, ValueTypes.BOOLEAN, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:challenges/tool_for_obsidian\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneTransmission(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        Level level = helper.getLevel();\n\n        // Redstone reader at POS facing west, reading from a redstone block (value 15)\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.west(), Blocks.REDSTONE_BLOCK);\n        PartHelpers.addPart(level, helper.absolutePos(POS), Direction.WEST,\n                PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartPos readerPos = PartPos.of(level, helper.absolutePos(POS), Direction.WEST);\n\n        // Redstone writer at POS.east() facing east\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS.east()), Direction.EAST,\n                PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        PartPos writerPos = PartPos.of(level, helper.absolutePos(POS.east()), Direction.EAST);\n\n        // Create variable card from the redstone reader for the INTEGER_VALUE aspect\n        ItemStack readerCard = createVariableFromReader(level, readerPos, Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer slot and activate with player to fire the event\n        placeVariableInWriterWithPlayer(level, writerPos, Aspects.Write.Redstone.INTEGER, readerCard, player);\n\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:aspects/redstone_transmission\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementSmartPressurePlate(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        Level level = helper.getLevel();\n\n        // Entity reader at POS facing west\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS), Direction.WEST,\n                PartTypes.ENTITY_READER, new ItemStack(PartTypes.ENTITY_READER.getItem()));\n        PartPos readerPos = PartPos.of(level, helper.absolutePos(POS), Direction.WEST);\n\n        // Redstone writer at POS.east() facing east\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS.east()), Direction.EAST,\n                PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        PartPos writerPos = PartPos.of(level, helper.absolutePos(POS.east()), Direction.EAST);\n\n        // Variable store adjacent to cable\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore varStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Create entity variable card from reader\n        ItemStack entityCard = createVariableFromReader(level, readerPos, Aspects.Read.Entity.ENTITY);\n        int entityId = getVariableFacade(level, entityCard).getId();\n        varStore.getInventory().setItem(0, entityCard);\n\n        // Create NAMED_NAME operator variable referencing entity variable\n        ItemStack nameCard = createVariableForOperator(level, Operators.NAMED_NAME, new int[]{entityId});\n        int nameId = getVariableFacade(level, nameCard).getId();\n        varStore.getInventory().setItem(1, nameCard);\n\n        // Create a dummy string variable for the second input of RELATIONAL_EQUALS\n        ItemStack dummyStringCard = createVariableForValue(level, ValueTypes.STRING,\n                ValueTypeString.ValueString.of(\"\"));\n        int dummyStringId = getVariableFacade(level, dummyStringCard).getId();\n        varStore.getInventory().setItem(2, dummyStringCard);\n\n        // Create RELATIONAL_EQUALS operator with [nameId, dummyStringId] inputs\n        ItemStack relEqualsCard = createVariableForOperator(level, Operators.RELATIONAL_EQUALS,\n                new int[]{nameId, dummyStringId});\n\n        // Place in writer and activate with player\n        placeVariableInWriterWithPlayer(level, writerPos, Aspects.Write.Redstone.BOOLEAN, relEqualsCard, player);\n\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:challenges/smart_pressure_plate\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementSpongeStepSound(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        Level level = helper.getLevel();\n\n        // Cable at POS with audio writer facing west\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS), Direction.WEST,\n                PartTypes.AUDIO_WRITER, new ItemStack(PartTypes.AUDIO_WRITER.getItem()));\n        PartPos writerPos = PartPos.of(level, helper.absolutePos(POS), Direction.WEST);\n\n        // Variable store adjacent to cable\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore varStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Create wet sponge block value variable\n        ItemStack spongeCard = createVariableForValue(level, ValueTypes.OBJECT_BLOCK,\n                ValueObjectTypeBlock.ValueBlock.of(Blocks.WET_SPONGE.defaultBlockState()));\n        int spongeId = getVariableFacade(level, spongeCard).getId();\n        varStore.getInventory().setItem(0, spongeCard);\n\n        // Create BLOCK_STEPSOUND operator variable referencing the sponge variable\n        ItemStack stepSoundCard = createVariableForOperator(level, Operators.OBJECT_BLOCK_STEPSOUND,\n                new int[]{spongeId});\n\n        // Place in audio writer for STRING_SOUND aspect and activate with player\n        placeVariableInWriterWithPlayer(level, writerPos, Aspects.Write.Audio.STRING_SOUND, stepSoundCard, player);\n\n        helper.succeedWhen(() -> assertAdvancement(helper, player, \"integrateddynamics:challenges/sponge_step_sound\"));\n    }\n\n    // ---- Negative Tests ----\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRootNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire ItemCraftedEvent with a vanilla item (not an integrateddynamics item) - should NOT trigger root\n        ItemStack stick = new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:stick\")));\n        NeoForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, stick, null));\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:root\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMeneglinDiscoveryNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Add a vanilla log instead of menril_log - should NOT trigger meneglin_discovery\n        ItemStack oakLog = new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:oak_log\")));\n        player.addItem(oakLog);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:meneglin_basics/meneglin_discovery\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMenrilProductionNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Add a vanilla block instead of crystalized_menril_block - should NOT trigger menril_production\n        ItemStack oakPlanks = new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:oak_planks\")));\n        player.addItem(oakPlanks);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:meneglin_basics/menril_production\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementSqueezingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft drying_basin instead of squeezer - should NOT trigger squeezing\n        fireItemCraftedTrigger(player, \"integrateddynamics:drying_basin\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:meneglin_basics/squeezing\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementDryingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft squeezer instead of drying_basin - should NOT trigger drying\n        fireItemCraftedTrigger(player, \"integrateddynamics:squeezer\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:meneglin_basics/drying\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementCablesLogicNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft wrench instead of cable - should NOT trigger cables_logic\n        fireItemCraftedTrigger(player, \"integrateddynamics:wrench\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:network_foundations/cables_logic\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMenrilWrenchingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft cable instead of wrench - should NOT trigger menril_wrenching\n        fireItemCraftedTrigger(player, \"integrateddynamics:cable\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:network_foundations/menril_wrenching\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariablesNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft variable_transformer_input instead of variable - should NOT trigger variables\n        fireItemCraftedTrigger(player, \"integrateddynamics:variable_transformer_input\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:network_foundations/variables\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableInputNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft variable instead of variable_transformer_input - should NOT trigger variable_input\n        fireItemCraftedTrigger(player, \"integrateddynamics:variable\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:network_foundations/variable_input\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableOutputNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft variable instead of variable_transformer_output - should NOT trigger variable_output\n        fireItemCraftedTrigger(player, \"integrateddynamics:variable\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:network_foundations/variable_output\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementNetworksLogicNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Place only 9 connected cables (not the required 10) - should NOT trigger networks_logic\n        for (int i = 0; i < 9; i++) {\n            helper.setBlock(POS.east(i), RegistryEntries.BLOCK_CABLE.value());\n        }\n        INetwork network = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n        NeoForge.EVENT_BUS.post(new NetworkInitializedEvent(\n                network, helper.getLevel(), helper.absolutePos(POS), player));\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:network_foundations/networks_logic\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementBlockReadingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft inventory_reader instead of block_reader - should NOT trigger block_reading\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_inventory_reader\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:basic_network_components/block_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementInventoryReadingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft block_reader instead of inventory_reader - should NOT trigger inventory_reading\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_block_reader\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:basic_network_components/inventory_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementMachineReadingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft block_reader instead of machine_reader - should NOT trigger machine_reading\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_block_reader\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:basic_network_components/machine_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneReadingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft block_reader instead of redstone_reader - should NOT trigger redstone_reading\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_block_reader\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:basic_network_components/redstone_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementValueDisplayingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Craft block_reader instead of display_panel - should NOT trigger value_displaying\n        fireItemCraftedTrigger(player, \"integrateddynamics:part_block_reader\");\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:basic_network_components/value_displaying\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLogicProgrammingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Open a different container (player inventory) - should NOT trigger logic_programming\n        NeoForge.EVENT_BUS.post(new PlayerContainerEvent.Open(player, player.inventoryMenu));\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:logic_operations/logic_programming\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementConstantDefinitionNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Create a BOOLEAN variable at logic_programmer (not integer type) - should NOT trigger constant_definition\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.BOOLEAN, ValueTypeBoolean.ValueBoolean.of(false));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get());\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:logic_operations/constant_definition\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableMaterializationNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Create integer variable at PROXY (not materializer) - should NOT trigger variable_materialization\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_PROXY.get());\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:advanced_network_components/variable_materialization\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementVariableProxyingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Create integer variable at MATERIALIZER (not proxy) - should NOT trigger variable_proxying\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_MATERIALIZER.get());\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:advanced_network_components/variable_proxying\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecipeCreationNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Create INTEGER variable at logic_programmer (not recipe type) - should NOT trigger recipe_creation\n        ItemStack card = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        IVariableFacade facade = getVariableFacade(helper.getLevel(), card);\n        fireVariableCreatedTrigger(player, facade, RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get());\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:recipe_handling/recipe_creation\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneCapturingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire reader aspect event with machine_reader (not redstone_reader) - should NOT trigger redstone_capturing\n        fireReaderAspectEvent(helper, player, POS, PartTypes.MACHINE_READER, Aspects.Read.Machine.LIST_GETRECIPES);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:aspects/redstone_capturing\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecipeReadingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire reader aspect event with redstone_reader (not machine_reader) - should NOT trigger recipe_reading\n        fireReaderAspectEvent(helper, player, POS, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.INTEGER_VALUE);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:recipe_handling/recipe_reading\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneObservementNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire variable driven event with block NBT aspect (not redstone value aspect) - should NOT trigger redstone_observement\n        IAspectVariable<ValueTypeNbt.ValueNbt> aspectVar = makeAspectVar(Aspects.Read.Block.NBT);\n        fireVariableDrivenEvent(player, aspectVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:aspects/redstone_observement\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementArithmeticAdditionNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire variable driven event with OPERATOR_APPLY (not arithmetic_addition) - should NOT trigger arithmetic_addition\n        LazyExpression<ValueTypeOperator.ValueOperator> opVar =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.OPERATOR);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:logic_operations/arithmetic_addition\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementItemOriginIdentificationNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire variable driven event with ARITHMETIC_ADDITION (not itemstack_mod) - should NOT trigger item_origin_identification\n        LazyExpression<ValueTypeInteger.ValueInteger> opVar =\n                makeOpVar(Operators.ARITHMETIC_ADDITION, ValueTypes.INTEGER);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:advanced_operations/item_origin_identification\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLogicalListBuildingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire variable driven event with a list [1, 10, 99] (not [1, 10, 100]) - should NOT trigger logical_list_building\n        ValueTypeList.ValueList listValue = ValueTypeList.ValueList.ofList(ValueTypes.INTEGER,\n                Arrays.asList(\n                        ValueTypeInteger.ValueInteger.of(1),\n                        ValueTypeInteger.ValueInteger.of(10),\n                        ValueTypeInteger.ValueInteger.of(99)));\n        Variable<ValueTypeList.ValueList> listVar = new Variable<>(ValueTypes.LIST, listValue);\n        fireVariableDrivenEvent(player, listVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:advanced_operations/logical_list_building\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementWhatWouldIBeLookingAtNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire variable driven event with ARITHMETIC_ADDITION (not entity_targetblock) - should NOT trigger what_would_i_be_looking_at\n        LazyExpression<ValueTypeInteger.ValueInteger> opVar =\n                makeOpVar(Operators.ARITHMETIC_ADDITION, ValueTypes.INTEGER);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecipeLookupNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Same operator_apply with recipe aspect, but iron_ingot instead of chest - should NOT trigger recipe_lookup\n        IAspectVariable<ValueTypeOperator.ValueOperator> input0 =\n                makeAspectVar(Aspects.Read.Machine.OPERATOR_GETRECIPEBYOUTPUT);\n        ValueObjectTypeItemStack.ValueItemStack itemValue = ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(\"minecraft:iron_ingot\")), 1));\n        Variable<ValueObjectTypeItemStack.ValueItemStack> input1 =\n                new Variable<>(ValueTypes.OBJECT_ITEMSTACK, itemValue);\n        LazyExpression<ValueTypeOperator.ValueOperator> opVar =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.OPERATOR, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:recipe_handling/recipe_lookup\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLecternDataNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Fire variable driven event with redstone aspect (not block NBT aspect) - should NOT trigger lectern_data\n        IAspectVariable<ValueTypeInteger.ValueInteger> aspectVar =\n                makeAspectVar(Aspects.Read.Redstone.INTEGER_VALUE);\n        fireVariableDrivenEvent(player, aspectVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:nbt/lectern_data\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLecternBookNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Same operator with NBT aspect but wrong key \"Page\" (not \"Book\") - should NOT trigger lectern_book\n        IAspectVariable<ValueTypeNbt.ValueNbt> input0 = makeAspectVar(Aspects.Read.Block.NBT);\n        Variable<ValueTypeString.ValueString> input1 =\n                new Variable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"Page\"));\n        LazyExpression<ValueTypeNbt.ValueNbt> opVar =\n                makeOpVar(Operators.NBT_COMPOUND_VALUE_COMPOUND, ValueTypes.NBT, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:nbt/lectern_book\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementLecternBookNameNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Same operator with wrong path \".Book.title\" (not \".Book.id\") - should NOT trigger lectern_book_name\n        Variable<ValueTypeString.ValueString> input0 =\n                new Variable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\".Book.title\"));\n        IAspectVariable<ValueTypeNbt.ValueNbt> input1 = makeAspectVar(Aspects.Read.Block.NBT);\n        LazyExpression<ValueTypeNbt.ValueNbt> opVar =\n                makeOpVar(Operators.NBT_PATH_MATCH_FIRST, ValueTypes.NBT, input0, input1);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:nbt/lectern_book_name\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementDynamicAdditionsNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // OPERATOR_APPLY with inner OPERATOR_APPLY but STRING input (not INTEGER) - should NOT trigger dynamic_additions\n        LazyExpression<ValueTypeOperator.ValueOperator> inner =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.OPERATOR);\n        Variable<ValueTypeString.ValueString> stringVar =\n                new Variable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"\"));\n        LazyExpression<ValueTypeString.ValueString> opVar =\n                makeOpVar(Operators.OPERATOR_APPLY, ValueTypes.STRING, inner, stringVar);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:the_value_of_operators/dynamic_additions\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementDynamicListFilteringNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // operator_filter with result list [20] (not [10]) - should NOT trigger dynamic_list_filtering\n        ValueTypeList.ValueList resultList = ValueTypeList.ValueList.ofList(ValueTypes.INTEGER,\n                Arrays.asList(ValueTypeInteger.ValueInteger.of(20)));\n        ILazyExpressionValueCache cache = simpleCache();\n        LazyExpression<ValueTypeList.ValueList> opVar =\n                new LazyExpression<ValueTypeList.ValueList>(0, Operators.OPERATOR_FILTER, new IVariable[0], cache) {\n                    @Override public IValueType<ValueTypeList.ValueList> getType() { return ValueTypes.LIST; }\n                    @Override public ValueTypeList.ValueList getValue() { return resultList; }\n                };\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:the_value_of_operators/dynamic_list_filtering\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementCreeperTamingNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Use SHEEP instead of CREEPER - should NOT trigger creeper_taming\n        net.minecraft.world.entity.Entity sheep = helper.spawn(EntityType.SHEEP, POS.above());\n        Variable<ValueObjectTypeEntity.ValueEntity> sheepVar = new Variable<>(\n                ValueTypes.OBJECT_ENTITY, ValueObjectTypeEntity.ValueEntity.of(sheep));\n        LazyExpression<ValueTypeDouble.ValueDouble> opVar =\n                makeOpVar(Operators.OBJECT_ENTITY_HEALTH, ValueTypes.DOUBLE, sheepVar);\n        fireVariableDrivenEvent(player, opVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:challenges/creeper_taming\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRecursiveRecursionNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        // Use a finite list (not infinite) - should NOT trigger recursive_recursion\n        ValueTypeList.ValueList finiteList = ValueTypeList.ValueList.ofList(ValueTypes.INTEGER,\n                Arrays.asList(ValueTypeInteger.ValueInteger.of(1), ValueTypeInteger.ValueInteger.of(2)));\n        Variable<ValueTypeList.ValueList> listVar = new Variable<>(ValueTypes.LIST, finiteList);\n        fireVariableDrivenEvent(player, listVar);\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:challenges/recursive_recursion\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementRedstoneTransmissionNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        Level level = helper.getLevel();\n\n        // Same setup but STONE instead of REDSTONE_BLOCK (gives value 0, not 15)\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.west(), Blocks.STONE);\n        PartHelpers.addPart(level, helper.absolutePos(POS), Direction.WEST,\n                PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartPos readerPos = PartPos.of(level, helper.absolutePos(POS), Direction.WEST);\n\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS.east()), Direction.EAST,\n                PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        PartPos writerPos = PartPos.of(level, helper.absolutePos(POS.east()), Direction.EAST);\n\n        ItemStack readerCard = createVariableFromReader(level, readerPos, Aspects.Read.Redstone.INTEGER_VALUE);\n        placeVariableInWriterWithPlayer(level, writerPos, Aspects.Write.Redstone.INTEGER, readerCard, player);\n\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:aspects/redstone_transmission\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementSmartPressurePlateNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        Level level = helper.getLevel();\n\n        // Same redstone writer setup but use a plain boolean variable (not the required operator chain)\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS.east()), Direction.EAST,\n                PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        PartPos writerPos = PartPos.of(level, helper.absolutePos(POS.east()), Direction.EAST);\n\n        ItemStack boolCard = createVariableForValue(level, ValueTypes.BOOLEAN,\n                ValueTypeBoolean.ValueBoolean.of(true));\n        placeVariableInWriterWithPlayer(level, writerPos, Aspects.Write.Redstone.BOOLEAN, boolCard, player);\n\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:challenges/smart_pressure_plate\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAdvancementSpongeStepSoundNegative(GameTestHelper helper) {\n        ServerPlayer player = mockServerPlayer(helper);\n        Level level = helper.getLevel();\n\n        // Same setup but with dry SPONGE (not WET_SPONGE) - should NOT trigger sponge_step_sound\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(level, helper.absolutePos(POS), Direction.WEST,\n                PartTypes.AUDIO_WRITER, new ItemStack(PartTypes.AUDIO_WRITER.getItem()));\n        PartPos writerPos = PartPos.of(level, helper.absolutePos(POS), Direction.WEST);\n\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore varStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        ItemStack spongeCard = createVariableForValue(level, ValueTypes.OBJECT_BLOCK,\n                ValueObjectTypeBlock.ValueBlock.of(Blocks.SPONGE.defaultBlockState()));\n        int spongeId = getVariableFacade(level, spongeCard).getId();\n        varStore.getInventory().setItem(0, spongeCard);\n\n        ItemStack stepSoundCard = createVariableForOperator(level, Operators.OBJECT_BLOCK_STEPSOUND,\n                new int[]{spongeId});\n        placeVariableInWriterWithPlayer(level, writerPos, Aspects.Write.Audio.STRING_SOUND, stepSoundCard, player);\n\n        helper.succeedWhen(() -> assertAdvancementNotDone(helper, player, \"integrateddynamics:challenges/sponge_step_sound\"));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadAudio.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.NoteBlock;\nimport net.minecraft.world.level.block.state.properties.NoteBlockInstrument;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\n\npublic class GameTestsAspectsReadAudio {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioHarp(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.HARP);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_HARP_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioBassdrum(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.BASEDRUM);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_BASEDRUM_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioSnare(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.SNARE);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_SNARE_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioHat(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.HAT);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_HAT_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioBass(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.BASS);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_BASS_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioFlute(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.FLUTE);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_FLUTE_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioBell(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.BELL);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_BELL_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioGuitar(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.GUITAR);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_GUITAR_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioChime(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.CHIME);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_CHIME_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioXylophone(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.XYLOPHONE);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_XYLOPHONE_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioIronXylophone(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.IRON_XYLOPHONE);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_IRON_XYLOPHONE_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioCowBell(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.COW_BELL);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_COW_BELL_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioDidgeridoo(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.DIDGERIDOO);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_DIDGERIDOO_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioBit(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.BIT);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_BIT_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioBanjo(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.BANJO);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_BANJO_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioPling(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.PLING);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_PLING_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioZombie(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.ZOMBIE);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_ZOMBIE_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioSkeleton(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.SKELETON);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_SKELETON_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioCreeper(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.CREEPER);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_CREEPER_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioDragon(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.DRAGON);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_DRAGON_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioWitherSkeleton(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.WITHER_SKELETON);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_WITHER_SKELETON_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioPiglin(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.PIGLIN);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_PIGLIN_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadAudioCustomHead(GameTestHelper helper) {\n        playNote(helper, NoteBlockInstrument.CUSTOM_HEAD);\n        testReadAspect(POS, helper, PartTypes.AUDIO_READER, Aspects.Read.Audio.INTEGER_CUSTOM_HEAD_NOTE, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    public static void playNote(GameTestHelper helper, NoteBlockInstrument instrument) {\n        helper.setBlock(POS.west(), Blocks.NOTE_BLOCK.defaultBlockState()\n                .setValue(NoteBlock.INSTRUMENT, instrument)\n                .setValue(NoteBlock.NOTE, 15));\n        helper.getBlockState(POS.west()).triggerEvent(helper.getLevel(), helper.absolutePos(POS), 0, 0);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadBlock.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\n\npublic class GameTestsAspectsReadBlock {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockBooleanBlockTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.BOOLEAN_BLOCK, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockBooleanBlockFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.BOOLEAN_BLOCK, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockDimension(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.STRING_DIMENSION, ValueTypeString.ValueString.of(helper.getLevel().dimension().identifier().toString()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockPosX(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.INTEGER_POSX, ValueTypeInteger.ValueInteger.of(helper.absolutePos(POS.west()).getX()));\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockPosY(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.INTEGER_POSY, ValueTypeInteger.ValueInteger.of(helper.absolutePos(POS.west()).getY()));\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockPosZ(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.INTEGER_POSZ, ValueTypeInteger.ValueInteger.of(helper.absolutePos(POS.west()).getZ()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockBlock(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.BLOCK, ValueObjectTypeBlock.ValueBlock.of(Blocks.STONE.defaultBlockState()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockNbt(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        CompoundTag tag = helper.getBlockEntity(POS.west(), BlockEntity.class)\n                .saveWithFullMetadata(helper.getLevel().registryAccess());\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.NBT, ValueTypeNbt.ValueNbt.of(tag));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockBiome(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.STRING_DIMENSION, ValueTypeString.ValueString.of(\"minecraft:overworld\"));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadBlockLight(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.BLOCK_READER, Aspects.Read.Block.INTEGER_LIGHT, ValueTypeInteger.ValueInteger.of(helper.getLevel().getMaxLocalRawBrightness(helper.absolutePos(POS.west()))));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadEntity.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.animal.chicken.Chicken;\nimport net.minecraft.world.entity.animal.cow.Cow;\nimport net.minecraft.world.entity.decoration.ItemFrame;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeEntity;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.Objects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspectPredicate;\n\npublic class GameTestsAspectsReadEntity {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityItemFrameRotationInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.INTEGER_ITEMFRAMEROTATION, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityItemFrameRotationValid(GameTestHelper helper) {\n        ItemFrame itemFrame = helper.spawn(EntityType.ITEM_FRAME, POS.west());\n        itemFrame.setRotation(3);\n        itemFrame.setYRot(Direction.WEST.toYRot());\n        testReadAspect(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.INTEGER_ITEMFRAMEROTATION, ValueTypeInteger.ValueInteger.of(3));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityItemFrameContentsInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.ITEMSTACK_ITEMFRAMECONTENTS, ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityItemFrameContentsValid(GameTestHelper helper) {\n        ItemFrame itemFrame = helper.spawn(EntityType.ITEM_FRAME, POS.west());\n        itemFrame.setItem(new ItemStack(Items.COAL));\n        itemFrame.setYRot(Direction.WEST.toYRot());\n        testReadAspect(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.ITEMSTACK_ITEMFRAMECONTENTS, ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.COAL)));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityEntities(GameTestHelper helper) {\n        Cow cow = helper.spawn(EntityType.COW, POS.west());\n        Chicken chicken = helper.spawn(EntityType.CHICKEN, POS.west());\n        testReadAspect(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.LIST_ENTITIES, ValueTypeList.ValueList.ofAll(\n                ValueObjectTypeEntity.ValueEntity.of(cow),\n                ValueObjectTypeEntity.ValueEntity.of(chicken)\n        ));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityEntity(GameTestHelper helper) {\n        Cow cow = helper.spawn(EntityType.COW, POS.west());\n        testReadAspect(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.ENTITY, ValueObjectTypeEntity.ValueEntity.of(cow));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadEntityPlayers(GameTestHelper helper) {\n        testReadAspectPredicate(POS, helper, PartTypes.ENTITY_READER, Aspects.Read.Entity.LIST_PLAYERS, Objects::nonNull);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadExtraDimensional.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeDouble;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.Objects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspectPredicate;\n\npublic class GameTestsAspectsReadExtraDimensional {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadExtraDimensionalRandom(GameTestHelper helper) {\n        testReadAspectPredicate(POS, helper, PartTypes.EXTRADIMENSIONAL_READER, Aspects.Read.ExtraDimensional.INTEGER_RANDOM, Objects::nonNull);\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadExtraDimensionalPlayerCount(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.EXTRADIMENSIONAL_READER, Aspects.Read.ExtraDimensional.INTEGER_PLAYERCOUNT, ValueTypeInteger.ValueInteger.of(ServerLifecycleHooks.getCurrentServer().getPlayerCount()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadExtraDimensionalTickTime(GameTestHelper helper) {\n        testReadAspectPredicate(POS, helper, PartTypes.EXTRADIMENSIONAL_READER, Aspects.Read.ExtraDimensional.INTEGER_TICKTIME, Objects::nonNull);\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadExtraDimensionalTps(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.EXTRADIMENSIONAL_READER, Aspects.Read.ExtraDimensional.DOUBLE_TPS, ValueTypeDouble.ValueDouble.of(20));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadExtraDimensionalPlayers(GameTestHelper helper) {\n        testReadAspectPredicate(POS, helper, PartTypes.EXTRADIMENSIONAL_READER, Aspects.Read.ExtraDimensional.LIST_PLAYERS, Objects::nonNull);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadFluid.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.material.Fluids;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDryingBasin;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\n\npublic class GameTestsAspectsReadFluid {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidFullTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 1_000));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_FULL, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidFullFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 10));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_FULL, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidEmptyTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_EMPTY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidEmptyFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 10));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_EMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidNonEmptyTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 10));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_NONEMPTY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidNonEmptyFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_NONEMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidApplicableTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_APPLICABLE, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidApplicableFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BOOLEAN_APPLICABLE, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidAmount(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 10));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LONG_AMOUNT, ValueTypeLong.ValueLong.of(10));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidAmountTotal(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 10));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LONG_AMOUNTTOTAL, ValueTypeLong.ValueLong.of(10));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidCapacityValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LONG_CAPACITY, ValueTypeLong.ValueLong.of(1_000));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidCapacityInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LONG_CAPACITY, ValueTypeLong.ValueLong.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidCapacityTotalValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LONG_CAPACITYTOTAL, ValueTypeLong.ValueLong.of(1_000));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidCapacityTotalInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LONG_CAPACITYTOTAL, ValueTypeLong.ValueLong.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidTanksValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.INTEGER_TANKS, ValueTypeInteger.ValueInteger.of(1));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidTanksInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.INTEGER_TANKS, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidFillRatio(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 500));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.DOUBLE_FILLRATIO, ValueTypeDouble.ValueDouble.of(0.5D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidTankFluids(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 500));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LIST_TANKFLUIDS, ValueTypeList.ValueList.ofAll(\n                ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 500))\n        ));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidTankCapacities(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 500));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.LIST_TANKCAPACITIES, ValueTypeList.ValueList.ofAll(\n                ValueTypeInteger.ValueInteger.of(1_000)\n        ));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidFluidStack(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_DRYING_BASIN.get());\n        BlockEntityDryingBasin dryingBasin = helper.getBlockEntity(POS.west(), BlockEntityDryingBasin.class);\n        dryingBasin.getTank().setFluid(new FluidStack(Fluids.WATER, 500));\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.FLUIDSTACK, ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 500)));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidBlockValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.WATER);\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BLOCK, ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 1_000)));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadFluidBlockInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.FLUID_READER, Aspects.Read.Fluid.BLOCK, ValueObjectTypeFluidStack.ValueFluidStack.of(FluidStack.EMPTY));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadInventory.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.ChestBlockEntity;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\n\npublic class GameTestsAspectsReadInventory {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryFullTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        for (int i = 0; i < 27; i++) {\n            chest.setItem(i, new ItemStack(Items.COAL));\n        }\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_FULL, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryFullFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        for (int i = 0; i < 26; i++) {\n            chest.setItem(i, new ItemStack(Items.COAL));\n        }\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_FULL, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryEmptyTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_EMPTY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryEmptyFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL));\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_EMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryNonEmptyTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL));\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_NONEMPTY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryNonEmptyFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_NONEMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryApplicableTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_APPLICABLE, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryApplicableFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.BOOLEAN_APPLICABLE, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryCount(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL, 10));\n        chest.setItem(1, new ItemStack(Items.COAL, 5));\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.INTEGER_COUNT, ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventorySlotsValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.INTEGER_SLOTS, ValueTypeInteger.ValueInteger.of(27));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventorySlotsInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.INTEGER_SLOTS, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventorySlotsFilled(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL, 10));\n        chest.setItem(1, new ItemStack(Items.COAL, 5));\n        chest.setItem(10, new ItemStack(Items.COAL, 5));\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.INTEGER_SLOTSFILLED, ValueTypeInteger.ValueInteger.of(3));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryFillRatio(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL, 10));\n        chest.setItem(1, new ItemStack(Items.COAL, 5));\n        chest.setItem(10, new ItemStack(Items.COAL, 5));\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.DOUBLE_FILLRATIO, ValueTypeDouble.ValueDouble.of(3d / 27d));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadInventoryItemStacks(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL, 10));\n        chest.setItem(1, new ItemStack(Items.COAL, 5));\n        chest.setItem(10, new ItemStack(Items.COAL, 5));\n        testReadAspect(POS, helper, PartTypes.INVENTORY_READER, Aspects.Read.Inventory.LIST_ITEMSTACKS, ValueTypeList.ValueList.ofAll(\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.COAL, 10)),\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.COAL, 5)),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.COAL, 5)),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY)\n        ));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadMachine.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.FurnaceBlockEntity;\nimport org.cyclops.commoncapabilities.IngredientComponents;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsAspectsReadMachine {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsWorkerFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISWORKER, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsWorkerTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISWORKER, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineHasWorkFalseInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_HASWORK, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineHasWorkFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_HASWORK, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineHasWorkTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        FurnaceBlockEntity machine = helper.getBlockEntity(POS.west(), FurnaceBlockEntity.class);\n        machine.setItem(0, new ItemStack(Items.RAW_IRON));\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_HASWORK, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanWorkFalseInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANWORK, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanWorkFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANWORK, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanWorkTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        FurnaceBlockEntity machine = helper.getBlockEntity(POS.west(), FurnaceBlockEntity.class);\n        machine.setItem(1, new ItemStack(Items.COAL));\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANWORK, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsWorkingFalseInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISWORKING, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsWorkingFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISWORKING, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsWorkingTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        FurnaceBlockEntity machine = helper.getBlockEntity(POS.west(), FurnaceBlockEntity.class);\n        machine.setItem(0, new ItemStack(Items.RAW_IRON));\n        machine.setItem(1, new ItemStack(Items.COAL));\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISWORKING, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsTemperatureFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISTEMPERATURE, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsTemperatureTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISTEMPERATURE, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineTemperatureInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_TEMPERATURE, ValueTypeDouble.ValueDouble.of(0D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineTemperature(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        FurnaceBlockEntity machine = helper.getBlockEntity(POS.west(), FurnaceBlockEntity.class);\n        machine.setItem(0, new ItemStack(Items.RAW_IRON));\n        machine.setItem(1, new ItemStack(Items.COAL));\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_TEMPERATURE, ValueTypeDouble.ValueDouble.of(273.15D + 1600D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineTemperatureWorking(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_TEMPERATURE, ValueTypeDouble.ValueDouble.of(273.15D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineMaxTemperatureInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_MAXTEMPERATURE, ValueTypeDouble.ValueDouble.of(0D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineMaxTemperature(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_MAXTEMPERATURE, ValueTypeDouble.ValueDouble.of(Double.MAX_VALUE));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineMinTemperatureInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_MINTEMPERATURE, ValueTypeDouble.ValueDouble.of(0D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineMinTemperature(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_MINTEMPERATURE, ValueTypeDouble.ValueDouble.of(273.15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineDefaultTemperatureInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_DEFAULTTEMPERATURE, ValueTypeDouble.ValueDouble.of(0D));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineDefaultTemperature(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_DEFAULTTEMPERATURE, ValueTypeDouble.ValueDouble.of(273.15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsRecipeHandlerFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISRECIPEHANDLER, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsRecipeHandlerTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISRECIPEHANDLER, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineRecipesInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.LIST_GETRECIPES, ValueTypeList.ValueList.ofAll());\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineRecipes(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        testReadAspectPredicate(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.LIST_GETRECIPES, list -> {\n            try {\n                return list.getRawValue().getLength() > 10;\n            } catch (EvaluationException e) {\n                throw new RuntimeException(e);\n            }\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeOutputInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEOUTPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.RAW_IRON)))),\n                ValueObjectTypeIngredients.ValueIngredients.of(null)\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeOutput(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        IMixedIngredients ingredients = MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.RAW_IRON));\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEOUTPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(ingredients)),\n                ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT)))\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeInputsInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEINPUTS,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT)))),\n                ValueTypeList.ValueList.ofAll()\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeInputs(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        IMixedIngredients ingredients = MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT));\n        testReadAspectOperatorPredicate(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEINPUTS,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(ingredients)),\n                value -> {\n                    try {\n                        return value instanceof ValueTypeList.ValueList<?,?> list && list.getRawValue().getLength() >= 3;\n                    } catch (EvaluationException e) {\n                        throw new RuntimeException(e);\n                    }\n                }\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipesByInputsInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPESBYINPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.RAW_IRON)))),\n                ValueTypeList.ValueList.ofAll()\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipesByInputsInputs(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        IMixedIngredients ingredients = MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.RAW_IRON));\n        testReadAspectOperatorPredicate(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPESBYINPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(ingredients)),\n                value -> {\n                    try {\n                        return value instanceof ValueTypeList.ValueList<?,?> list && list.getRawValue().getLength() == 1;\n                    } catch (EvaluationException e) {\n                        throw new RuntimeException(e);\n                    }\n                }\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipesByOutputInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPESBYOUTPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT)))),\n                ValueTypeList.ValueList.ofAll()\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipesByOutput(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        IMixedIngredients ingredients = MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT));\n        testReadAspectOperatorPredicate(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPESBYOUTPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(ingredients)),\n                value -> {\n                    try {\n                        return value instanceof ValueTypeList.ValueList<?,?> list && list.getRawValue().getLength() == 3;\n                    } catch (EvaluationException e) {\n                        throw new RuntimeException(e);\n                    }\n                }\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeByInputsInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEBYINPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.RAW_IRON)))),\n                ValueObjectTypeRecipe.ValueRecipe.of(null)\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeByInputsInputs(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        IMixedIngredients ingredients = MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.RAW_IRON));\n        testReadAspectOperatorPredicate(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEBYINPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(ingredients)),\n                value -> value instanceof ValueObjectTypeRecipe.ValueRecipe recipe && recipe.getRawValue().isPresent()\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeByOutputInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspectOperator(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEBYOUTPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT)))),\n                ValueObjectTypeRecipe.ValueRecipe.of(null)\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineGetRecipeByOutput(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.FURNACE);\n        IMixedIngredients ingredients = MixedIngredients.ofInstance(IngredientComponents.ITEMSTACK, new ItemStack(Items.IRON_INGOT));\n        testReadAspectOperatorPredicate(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.OPERATOR_GETRECIPEBYOUTPUT,\n                Lists.newArrayList(ValueObjectTypeIngredients.ValueIngredients.of(ingredients)),\n                value -> value instanceof ValueObjectTypeRecipe.ValueRecipe recipe && recipe.getRawValue().isPresent()\n        );\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanExtractEnergyInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANEXTRACTENERGY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanExtractEnergyValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANEXTRACTENERGY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanExtractEnergyValidFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(0);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANEXTRACTENERGY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanInsertEnergyInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANINSERTENERGY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineCanInsertEnergyValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_CANINSERTENERGY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyFullInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYFULL, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyFullValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(1_000_000);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYFULL, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyFullValidFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYFULL, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyEmptyInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYEMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyEmptyValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(0);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYEMPTY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyEmptyValidFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYEMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyNonEmptyInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYNONEMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyNonEmptyValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYNONEMPTY, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineIsEnergyNonEmptyValidFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(0);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.BOOLEAN_ISENERGYNONEMPTY, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineEnergyStoredInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.INTEGER_ENERGYSTORED, ValueTypeLong.ValueLong.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineEnergyStoredValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.INTEGER_ENERGYSTORED, ValueTypeLong.ValueLong.of(100));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineEnergyCapacityInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.INTEGER_ENERGYCAPACITY, ValueTypeLong.ValueLong.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineEnergyCapacityValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.INTEGER_ENERGYCAPACITY, ValueTypeLong.ValueLong.of(1_000_000));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineEnergyFillRatioInvalid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.STONE);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_ENERGYFILLRATIO, ValueTypeDouble.ValueDouble.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadMachineEnergyFillRatioValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(500_000);\n        testReadAspect(POS, helper, PartTypes.MACHINE_READER, Aspects.Read.Machine.DOUBLE_ENERGYFILLRATIO, ValueTypeDouble.ValueDouble.of(0.5));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadNetwork.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeLong;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.function.Supplier;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsAspectsReadNetwork {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkApplicableTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.BOOLEAN_APPLICABLE, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkApplicableFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.BOOLEAN_APPLICABLE, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkElementCountInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.INTEGER_ELEMENT_COUNT, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkElementCountValid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.INTEGER_ELEMENT_COUNT, ValueTypeInteger.ValueInteger.of(2));\n\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.west()), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.west()), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkBatteryCountInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.INTEGER_ENERGY_BATTERY_COUNT, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkBatteryCountValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        helper.setBlock(POS.west().west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.INTEGER_ENERGY_BATTERY_COUNT, ValueTypeInteger.ValueInteger.of(2));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkEnergyStoredInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.LONG_ENERGY_STORED, ValueTypeLong.ValueLong.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkEnergyStoredValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        helper.setBlock(POS.west().west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class).setEnergyStored(100);\n        helper.getBlockEntity(POS.west().west(), BlockEntityEnergyBattery.class).setEnergyStored(200);\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.LONG_ENERGY_STORED, ValueTypeLong.ValueLong.of(300));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkEnergyMaxInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.LONG_ENERGY_MAX, ValueTypeLong.ValueLong.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkEnergyMaxValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        helper.setBlock(POS.west().west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        helper.getBlockEntity(POS.west(), BlockEntityEnergyBattery.class).setEnergyStored(100);\n        helper.getBlockEntity(POS.west().west(), BlockEntityEnergyBattery.class).setEnergyStored(200);\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.LONG_ENERGY_MAX, ValueTypeLong.ValueLong.of(2_000_000));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkEnergConsumptionRateInvalid(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.INTEGER_ENERGY_CONSUMPTION_RATE, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkEnergConsumptionRateValid(GameTestHelper helper) {\n        GeneralConfig.energyConsumptionMultiplier = 1;\n\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.INTEGER_ENERGY_CONSUMPTION_RATE);\n        helper.succeedWhen(() -> {\n            assertValueEqual(helper, variableSupplier.get(), ValueTypeInteger.ValueInteger.of(2));\n            GeneralConfig.energyConsumptionMultiplier = 0;\n        });\n\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.west()), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.west()), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkValueInvalidNoNetwork(GameTestHelper helper) {\n        testReadAspectThrows(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.ANY_VALUE);\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkValueInvalidNoPart(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_ENERGY_BATTERY.get());\n        testReadAspectThrows(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.ANY_VALUE);\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkValueInvalidWrongSide(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.west()), Direction.NORTH, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.west()), Direction.NORTH), Aspects.Write.Redstone.BOOLEAN, new ItemStack(RegistryEntries.ITEM_VARIABLE));\n        testReadAspectThrows(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.ANY_VALUE);\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkValueValid(GameTestHelper helper) {\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.west()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.west()), Direction.EAST), Aspects.Write.Redstone.BOOLEAN, new ItemStack(RegistryEntries.ITEM_VARIABLE));\n        testReadAspect(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.ANY_VALUE, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkGetVariableByIdInvalidId(GameTestHelper helper) {\n        // Create network reader and read OPERATOR_GETVARIABLEBYID\n        testReadAspectSetup(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.OPERATOR_GETVARIABLEBYID);\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Network.OPERATOR_GETVARIABLEBYID);\n\n        // Add cables\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        helper.setBlock(POS.north().west(), RegistryEntries.BLOCK_CABLE.get());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_CABLE.get());\n\n        // Add display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.north()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north().north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north().north(), BlockEntityVariablestore.class);\n\n        // Insert OPERATOR_GETVARIABLEBYID in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n\n        // Create a constant value 10, and place it in variable store\n        ItemStack variableConstant = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(10));\n        variableStore.getInventory().setItem(1, variableConstant);\n\n        // Create a variable referring the constant variable id, and place it in variable store\n        ItemStack variableConstantId = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(-1));\n        variableStore.getInventory().setItem(2, variableConstantId);\n\n        // Create variable card for OPERATOR_GETVARIABLEBYID operator on variable aspect\n        ItemStack variableApplied = createVariableForOperator(helper.getLevel(), Operators.OPERATOR_APPLY, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableConstantId).getId()\n        });\n\n        // Place applied variable in display panel\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.EAST), variableApplied);\n\n        helper.succeedWhen(() -> {\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), null);\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(-1))\n            ), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadNetworkGetVariableByIdValid(GameTestHelper helper) {\n        // Create network reader and read OPERATOR_GETVARIABLEBYID\n        testReadAspectSetup(POS, helper, PartTypes.NETWORK_READER, Aspects.Read.Network.OPERATOR_GETVARIABLEBYID);\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Network.OPERATOR_GETVARIABLEBYID);\n\n        // Add cables\n        helper.setBlock(POS.west(), RegistryEntries.BLOCK_CABLE.get());\n        helper.setBlock(POS.north().west(), RegistryEntries.BLOCK_CABLE.get());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_CABLE.get());\n\n        // Add display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.north()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north().north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north().north(), BlockEntityVariablestore.class);\n\n        // Insert OPERATOR_GETVARIABLEBYID in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n\n        // Create a constant value 10, and place it in variable store\n        ItemStack variableConstant = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(10));\n        variableStore.getInventory().setItem(1, variableConstant);\n\n        // Create a variable referring the constant variable id, and place it in variable store\n        ItemStack variableConstantId = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(getVariableFacade(helper.getLevel(), variableConstant).getId()));\n        variableStore.getInventory().setItem(2, variableConstantId);\n\n        // Create variable card for OPERATOR_GETVARIABLEBYID operator on variable aspect\n        ItemStack variableApplied = createVariableForOperator(helper.getLevel(), Operators.OPERATOR_APPLY, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableConstantId).getId()\n        });\n\n        // Place applied variable in display panel\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.EAST), variableApplied);\n\n        helper.succeedWhen(() -> {\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(10));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadRedstone.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.ChestBlockEntity;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.function.Supplier;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspectSetup;\n\npublic class GameTestsAspectsReadRedstone {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadRedstoneBooleanLow(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.REDSTONE_BLOCK);\n        testReadAspect(POS, helper, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.BOOLEAN_LOW, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadRedstoneBooleanNonLow(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.REDSTONE_BLOCK);\n        testReadAspect(POS, helper, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.BOOLEAN_NONLOW, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadRedstoneBooleanHigh(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.REDSTONE_BLOCK);\n        testReadAspect(POS, helper, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.BOOLEAN_HIGH, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadRedstoneBooleanClock(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.REDSTONE_BLOCK);\n        Supplier<IAspectVariable> variableSupplier = testReadAspectSetup(POS, helper, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.BOOLEAN_CLOCK);\n        helper.startSequence()\n                .thenWaitUntil(() -> GameTestHelpersIntegratedDynamics.assertValueEqual(helper, variableSupplier.get(), ValueTypeBoolean.ValueBoolean.of(false)))\n                .thenWaitUntil(() -> GameTestHelpersIntegratedDynamics.assertValueEqual(helper, variableSupplier.get(), ValueTypeBoolean.ValueBoolean.of(true)))\n                .thenSucceed();\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadRedstoneInteger(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.CHEST);\n        ChestBlockEntity chest = helper.getBlockEntity(POS.west(), ChestBlockEntity.class);\n        chest.setItem(0, new ItemStack(Items.COAL));\n        testReadAspect(POS, helper, PartTypes.REDSTONE_READER, Aspects.Read.Redstone.INTEGER_COMPARATOR, ValueTypeInteger.ValueInteger.of(1));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsReadWorld.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.clock.WorldClocks;\nimport net.minecraft.world.level.saveddata.WeatherData;\nimport net.minecraft.world.level.storage.ServerLevelData;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.function.Supplier;\n\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspect;\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.testReadAspectPredicate;\n\npublic class GameTestsAspectsReadWorld {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_clear\")\n    public void testAspectsReadWorldWeatherClearTrue(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_WEATHER_CLEAR, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_rain\")\n    public void testAspectsReadWorldWeatherClearFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_WEATHER_CLEAR, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_rain\")\n    public void testAspectsReadWorldWeatherRainingTrue(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_WEATHER_RAINING, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_clear\")\n    public void testAspectsReadWorldWeatherRainingFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_WEATHER_RAINING, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_thunder\")\n    public void testAspectsReadWorldWeatherThunderTrue(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_WEATHER_THUNDER, ValueTypeBoolean.ValueBoolean.of(true));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_clear\")\n    public void testAspectsReadWorldWeatherThunderFalse(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_WEATHER_THUNDER, ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":time_day\")\n    public void testAspectsReadWorldIsDayTrue(GameTestHelper helper) {\n        Supplier<IAspectVariable> variableSupplier = GameTestHelpersIntegratedDynamics.testReadAspectSetup(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_ISDAY);\n        helper.succeedWhen(() -> {\n            helper.getLevel().skyDarken = 3; // Corresponds to day. Should not be necessary, but without it, this test fails 1/20 times.\n            GameTestHelpersIntegratedDynamics.assertValueEqual(helper, variableSupplier.get(), ValueTypeBoolean.ValueBoolean.of(true));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":time_night\")\n    public void testAspectsReadWorldIsDayFalse(GameTestHelper helper) {\n        Supplier<IAspectVariable> variableSupplier = GameTestHelpersIntegratedDynamics.testReadAspectSetup(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_ISDAY);\n        helper.succeedWhen(() -> {\n            helper.getLevel().skyDarken = 5; // Corresponds to night. Should not be necessary, but without it, this test fails 1/20 times.\n            GameTestHelpersIntegratedDynamics.assertValueEqual(helper, variableSupplier.get(), ValueTypeBoolean.ValueBoolean.of(false));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":time_night\")\n    public void testAspectsReadWorldIsNightTrue(GameTestHelper helper) {\n        Supplier<IAspectVariable> variableSupplier = GameTestHelpersIntegratedDynamics.testReadAspectSetup(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_ISNIGHT);\n        helper.succeedWhen(() -> {\n            helper.getLevel().skyDarken = 5; // Corresponds to night. Should not be necessary, but without it, this test fails 1/20 times.\n            GameTestHelpersIntegratedDynamics.assertValueEqual(helper, variableSupplier.get(), ValueTypeBoolean.ValueBoolean.of(true));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":time_day\")\n    public void testAspectsReadWorldIsNightFalse(GameTestHelper helper) {\n        Supplier<IAspectVariable> variableSupplier = GameTestHelpersIntegratedDynamics.testReadAspectSetup(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.BOOLEAN_ISNIGHT);\n        helper.succeedWhen(() -> {\n            helper.getLevel().skyDarken = 3; // Corresponds to day. Should not be necessary, but without it, this test fails 1/20 times.\n            GameTestHelpersIntegratedDynamics.assertValueEqual(helper, variableSupplier.get(), ValueTypeBoolean.ValueBoolean.of(false));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":weather_clear2\")\n    public void testAspectsReadWorldRainCountdown(GameTestHelper helper) {\n        WeatherData weatherData = helper.getLevel().getWeatherData();\n        weatherData.setRaining(true);\n        weatherData.setRainTime(123);\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.INTEGER_RAINCOUNTDOWN, ValueTypeInteger.ValueInteger.of(123));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadWorldTicktime(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.INTEGER_TICKTIME, ValueTypeInteger.ValueInteger.of((int) (Helpers.mean(ServerLifecycleHooks.getCurrentServer().getTickTime(helper.getLevel().dimension())) * 1.0E-6D)));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":time_day_far\")\n    public void testAspectsReadWorldDaytime(GameTestHelper helper) {\n        long targetTicks = 1000 + 10L * IModHelpers.get().getMinecraftHelpers().getDayLength();\n        helper.getLevel().registryAccess().get(WorldClocks.OVERWORLD).ifPresent(clockHolder ->\n                ((ServerLevel) helper.getLevel()).clockManager().setTotalTicks(clockHolder, targetTicks));\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.INTEGER_DAYTIME, ValueTypeInteger.ValueInteger.of(1000));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadWorldLightlevel(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.INTEGER_LIGHTLEVEL, ValueTypeInteger.ValueInteger.of(helper.getLevel().getMaxLocalRawBrightness(helper.absolutePos(POS.west()))));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadWorldTps(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.DOUBLE_TPS, ValueTypeDouble.ValueDouble.of(20));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, environment = Reference.MOD_ID + \":time_day_far\")\n    public void testAspectsReadWorldTime(GameTestHelper helper) {\n        long targetTicks = 1000 + 10L * IModHelpers.get().getMinecraftHelpers().getDayLength();\n        helper.getLevel().registryAccess().get(WorldClocks.OVERWORLD).ifPresent(clockHolder ->\n                ((ServerLevel) helper.getLevel()).clockManager().setTotalTicks(clockHolder, targetTicks));\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.LONG_TIME, ValueTypeLong.ValueLong.of(1000 + 10 * IModHelpers.get().getMinecraftHelpers().getDayLength()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadWorldTotalTime(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.LONG_TOTALTIME, ValueTypeLong.ValueLong.of(helper.getLevel().getGameTime()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadWorldName(GameTestHelper helper) {\n        testReadAspect(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.STRING_NAME, ValueTypeString.ValueString.of(((ServerLevelData) helper.getLevel().getLevelData()).getLevelName()));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsReadWorldPlayers(GameTestHelper helper) {\n        testReadAspectPredicate(POS, helper, PartTypes.WORLD_READER, Aspects.Read.World.LIST_PLAYERS, players -> players.getRawValue() != null);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsAspectsWriteRedstone.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.RedStoneWireBlock;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.cyclops.integrateddynamics.part.aspect.write.AspectWriteBuilders;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsAspectsWriteRedstone {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneBooleanTrue(GameTestHelper helper) {\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        testWriteAspectSetup(POS, helper, PartTypes.REDSTONE_WRITER, Aspects.Write.Redstone.BOOLEAN, ValueTypeBoolean.ValueBoolean.of(true));\n        helper.succeedWhen(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneBooleanFalse(GameTestHelper helper) {\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        testWriteAspectSetup(POS, helper, PartTypes.REDSTONE_WRITER, Aspects.Write.Redstone.BOOLEAN, ValueTypeBoolean.ValueBoolean.of(false));\n        helper.succeedWhen(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneInteger(GameTestHelper helper) {\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        testWriteAspectSetup(POS, helper, PartTypes.REDSTONE_WRITER, Aspects.Write.Redstone.INTEGER, ValueTypeInteger.ValueInteger.of(10));\n        helper.succeedWhen(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 10));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneBooleanPulse(GameTestHelper helper) {\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.EAST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        testWriteAspectSetup(POS, helper, PartTypes.REDSTONE_WRITER, Aspects.Write.Redstone.BOOLEAN_PULSE, createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST), Aspects.Read.Redstone.BOOLEAN_CLOCK));\n        helper.startSequence()\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenSucceed();\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneIntegerPulse(GameTestHelper helper) {\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.EAST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableClock = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST), Aspects.Read.Redstone.BOOLEAN_CLOCK);\n        variableStore.getInventory().setItem(0, variableClock);\n\n        // Create 1 and 0 constants\n        ItemStack variable1 = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        ItemStack variable2 = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(3));\n        variableStore.getInventory().setItem(1, variable1);\n        variableStore.getInventory().setItem(2, variable2);\n\n        // Create variable card for choice operator that switches between 0 and 1\n        ItemStack variableChoice = createVariableForOperator(helper.getLevel(), Operators.GENERAL_CHOICE, new int[]{\n                getVariableFacade(helper.getLevel(), variableClock).getId(),\n                getVariableFacade(helper.getLevel(), variable1).getId(),\n                getVariableFacade(helper.getLevel(), variable2).getId()\n        });\n\n        testWriteAspectSetup(POS, helper, PartTypes.REDSTONE_WRITER, Aspects.Write.Redstone.INTEGER_PULSE, variableChoice);\n\n        helper.startSequence()\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenSucceed();\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneBooleanPulseLengthLong(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.EAST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Setup part with variable\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        PartPos partPos = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        ItemStack variableClock = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST), Aspects.Read.Redstone.BOOLEAN_CLOCK);\n        placeVariableInWriter(helper, helper.getLevel(), partPos, Aspects.Write.Redstone.BOOLEAN_PULSE, variableClock);\n\n        // Set pulse length to 10 ticks\n        setAspectProperty(partPos, Aspects.Write.Redstone.BOOLEAN_PULSE,\n                AspectWriteBuilders.Redstone.PROP_PULSE_LENGTH, ValueTypeInteger.ValueInteger.of(10));\n\n        helper.startSequence()\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15))\n                // Verify pulse is still active after several ticks\n                .thenIdle(5)\n                .thenExecute(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenSucceed();\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testAspectsWriteRedstoneIntegerPulseLengthLong(GameTestHelper helper) {\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.EAST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableClock = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST), Aspects.Read.Redstone.BOOLEAN_CLOCK);\n        variableStore.getInventory().setItem(0, variableClock);\n\n        // Create 0 and 3 constants\n        ItemStack variable1 = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        ItemStack variable2 = createVariableForValue(helper.getLevel(), ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(3));\n        variableStore.getInventory().setItem(1, variable1);\n        variableStore.getInventory().setItem(2, variable2);\n\n        // Create variable card for choice operator that switches between 0 and 3\n        ItemStack variableChoice = createVariableForOperator(helper.getLevel(), Operators.GENERAL_CHOICE, new int[]{\n                getVariableFacade(helper.getLevel(), variableClock).getId(),\n                getVariableFacade(helper.getLevel(), variable1).getId(),\n                getVariableFacade(helper.getLevel(), variable2).getId()\n        });\n\n        // Setup part with variable\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        PartPos partPos = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        placeVariableInWriter(helper, helper.getLevel(), partPos, Aspects.Write.Redstone.INTEGER_PULSE, variableChoice);\n\n        // Set pulse length to 10 ticks\n        setAspectProperty(partPos, Aspects.Write.Redstone.INTEGER_PULSE,\n                AspectWriteBuilders.Redstone.PROP_PULSE_LENGTH, ValueTypeInteger.ValueInteger.of(10));\n\n        helper.startSequence()\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15))\n                // Verify pulse is still active after several ticks\n                .thenIdle(5)\n                .thenExecute(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 15))\n                .thenWaitUntil(() -> helper.assertBlockProperty(POS.west(), RedStoneWireBlock.POWER, 0))\n                .thenSucceed();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsBattery.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.CrafterBlockEntity;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryConfig;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.capability.energystorage.SimpleEnergyHandlerCapacity;\n\npublic class GameTestsBattery {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testBatteryTransfer(GameTestHelper helper) {\n        // Place batteries\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        BlockEntityEnergyBattery battery1 = helper.getBlockEntity(POS, BlockEntityEnergyBattery.class);\n        BlockEntityEnergyBattery battery2 = helper.getBlockEntity(POS.north(), BlockEntityEnergyBattery.class);\n\n        // Fill battery 1\n        battery1.setEnergyStored(100_000);\n\n        // Enable transfer with redstone signal\n        helper.setBlock(POS.south(), Blocks.REDSTONE_TORCH);\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(battery1.getEnergyStored(), 0, Component.literal(\"Energy in battery 1 is not zero\"));\n            helper.assertValueEqual(battery2.getEnergyStored(), 100_000, Component.literal(\"Energy in battery 2 is not 5000\"));\n\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testBatteryCombineEmpty(GameTestHelper helper) {\n        // Set crafter\n        helper.setBlock(POS, Blocks.CRAFTER);\n        CrafterBlockEntity crafter = helper.getBlockEntity(POS, CrafterBlockEntity.class);\n        helper.setBlock(POS.north(), Blocks.REDSTONE_TORCH);\n\n        // Set recipe\n        crafter.setItem(0, new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY));\n        crafter.setItem(1, new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY));\n\n        helper.succeedWhen(() -> {\n            ItemStack result = helper.findOneEntity(EntityType.ITEM).getItem();\n            helper.assertValueEqual(result.getItem(), RegistryEntries.ITEM_ENERGY_BATTERY.get(), Component.literal(\"Result item is incorrect\"));\n            helper.assertValueEqual(result.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_CAPACITY), BlockEnergyBatteryConfig.capacity * 2, Component.literal(\"Result item capacity is incorrect\"));\n            helper.assertValueEqual(result.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE), 0, Component.literal(\"Result item energy content is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testBatteryCombineFilled(GameTestHelper helper) {\n        // Set crafter\n        helper.setBlock(POS, Blocks.CRAFTER);\n        CrafterBlockEntity crafter = helper.getBlockEntity(POS, CrafterBlockEntity.class);\n        helper.setBlock(POS.north(), Blocks.REDSTONE_TORCH);\n\n        // Set recipe\n        crafter.setItem(0, new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY));\n        crafter.setItem(1, new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY));\n\n        // Add some energy\n        crafter.getItem(0).set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE, 10_000);\n        crafter.getItem(1).set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE, 10_000);\n\n        helper.succeedWhen(() -> {\n            ItemStack result = helper.findOneEntity(EntityType.ITEM).getItem();\n            helper.assertValueEqual(result.getItem(), RegistryEntries.ITEM_ENERGY_BATTERY.get(), Component.literal(\"Result item is incorrect\"));\n            helper.assertValueEqual(result.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_CAPACITY), BlockEnergyBatteryConfig.capacity * 2, Component.literal(\"Result item capacity is incorrect\"));\n            helper.assertValueEqual(result.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE), 20_000, Component.literal(\"Result item energy content is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testBatteryCombineFilledHigherCapacity(GameTestHelper helper) {\n        // Set crafter\n        helper.setBlock(POS, Blocks.CRAFTER);\n        CrafterBlockEntity crafter = helper.getBlockEntity(POS, CrafterBlockEntity.class);\n        helper.setBlock(POS.north(), Blocks.REDSTONE_TORCH);\n\n        // Set recipe\n        crafter.setItem(0, new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY));\n        crafter.setItem(1, new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY));\n\n        // Set capacity\n        crafter.getItem(0).set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_CAPACITY, BlockEnergyBatteryConfig.capacity * 2);\n        crafter.getItem(1).set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_CAPACITY, BlockEnergyBatteryConfig.capacity * 2);\n\n        // Add some energy\n        crafter.getItem(0).set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE, 10_000);\n        crafter.getItem(1).set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE, 10_000);\n\n        helper.succeedWhen(() -> {\n            ItemStack result = helper.findOneEntity(EntityType.ITEM).getItem();\n            helper.assertValueEqual(result.getItem(), RegistryEntries.ITEM_ENERGY_BATTERY.get(), Component.literal(\"Result item is incorrect\"));\n            helper.assertValueEqual(result.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_CAPACITY), BlockEnergyBatteryConfig.capacity * 4, Component.literal(\"Result item capacity is incorrect\"));\n            helper.assertValueEqual(result.get(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_ENERGY_STORAGE), 20_000, Component.literal(\"Result item energy content is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testBatteryCapacityPersistence(GameTestHelper helper) {\n        // Place battery\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS, BlockEntityEnergyBattery.class);\n\n        // Set a custom capacity (simulating a crafted higher-capacity battery) and energy above the default capacity\n        int customCapacity = BlockEnergyBatteryConfig.capacity * 2;\n        int customEnergy = BlockEnergyBatteryConfig.capacity + 500_000;\n        battery.getEnergyHandler().setCapacity(customCapacity);\n        battery.setEnergyStored(customEnergy);\n\n        // Simulate save/load by serializing and deserializing the energy handler\n        TagValueOutput output = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING);\n        battery.getEnergyHandler().serialize(output);\n        CompoundTag tag = output.buildResult();\n\n        SimpleEnergyHandlerCapacity newHandler = new SimpleEnergyHandlerCapacity(BlockEnergyBatteryConfig.capacity);\n        ValueInput input = TagValueInput.create(ProblemReporter.DISCARDING, helper.getLevel().registryAccess(), tag);\n        newHandler.deserialize(input);\n\n        helper.assertValueEqual(newHandler.getCapacityAsInt(), customCapacity, Component.literal(\"Capacity was not persisted after save/load\"));\n        helper.assertValueEqual(newHandler.getAmountAsInt(), customEnergy, Component.literal(\"Energy was not persisted after save/load\"));\n        helper.succeed();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsCombinedAspects.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.RedStoneWireBlock;\nimport net.minecraft.world.level.block.state.properties.RedstoneSide;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsCombinedAspects {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderToWriter(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place redstone writer\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockProperty(POS.east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.east().east(), RedStoneWireBlock.POWER, 14);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)).getState();\n            helper.assertFalse(partStateWriter.isDeactivated(), Component.literal(\"Writer is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertValueEqual(partStateWriter.getActiveAspect(), Aspects.Write.Redstone.INTEGER, Component.literal(\"Active aspect is incorrect\"));\n            helper.assertTrue(partStateWriter.getErrors(Aspects.Write.Redstone.INTEGER).isEmpty(), Component.literal(\"Active aspect has errors\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsNothingToWriter(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone writer\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockProperty(POS.east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.east().east(), RedStoneWireBlock.POWER, 0);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)).getState();\n            helper.assertFalse(partStateWriter.isDeactivated(), Component.literal(\"Writer is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.INACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertTrue(partStateWriter.getActiveAspect() == null, Component.literal(\"Active aspect is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderToWriterDisconnected(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place redstone writer\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.east().east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockProperty(POS.east().east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.east().east().east(), RedStoneWireBlock.POWER, 0);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST)).getState();\n            IPartState partStateReader = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST)).getState();\n            helper.assertTrue(partStateWriter.isDeactivated(), Component.literal(\"Writer is not deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ERROR,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertValueEqual(partStateWriter.getActiveAspect(), Aspects.Write.Redstone.INTEGER, Component.literal(\"Active aspect is incorrect\"));\n            helper.assertValueEqual(partStateWriter.getErrors(Aspects.Write.Redstone.INTEGER), Lists.newArrayList(\n                    Component.translatable(L10NValues.VARIABLE_ERROR_PARTNOTINNETWORK, Integer.toString(partStateReader.getId()))\n            ), Component.literal(\"Active aspect errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderToDisplay(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableAspect);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertTrue(partAndState.getRight().getGlobalErrors().isEmpty(), Component.literal(\"Display panel has errors\"));\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(14));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsNothingToDisplay(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        helper.succeedWhen(() -> {\n            PartTypePanelDisplay.State partState = (PartTypePanelDisplay.State) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST)).getState();\n            helper.assertFalse(partState.isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.INACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partState.getDisplayValue(), null);\n            helper.assertValueEqual(partState.getGlobalErrors(), Lists.newArrayList(), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsBlankVariableToDisplay(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST), new ItemStack(RegistryEntries.ITEM_VARIABLE));\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), ValueTypeBoolean.ValueBoolean.of(true));\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplay(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableAdded);\n\n        helper.succeedWhen(() -> {\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(29));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsDisplayPanelAsVariableStore(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place two display panels that will act as variable stores\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_CABLE.value());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.north()), Direction.NORTH, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.north()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in display panels\n        placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.NORTH), variableAspect1);\n        placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.EAST), variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableAdded);\n\n        helper.succeedWhen(() -> {\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(29));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplayIncompleteVariableStore(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        // variableAspect2 not added in variable store!\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableAdded);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ERROR,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), null);\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect2).getId()))\n            ), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplayFullyDisconnected(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST), variableAdded);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ERROR,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), null);\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect1).getId())),\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect2).getId()))\n            ), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplayBecomesFullyDisconnected(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place temporary cable that will be removed next tick\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST), variableAdded);\n\n        helper.runAtTickTime(5, () -> helper.destroyBlock(POS.east()));\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ERROR,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), null);\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect1).getId())),\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect2).getId()))\n            ), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplayReadersDisconnected(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.east().east().north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.east().east().north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST), variableAdded);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ERROR,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), null);\n            IPartState partStateReader1 = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST)).getState();\n            IPartState partStateReader2 = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH)).getState();\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(\n                    Component.translatable(L10NValues.VARIABLE_ERROR_PARTNOTINNETWORK, Integer.toString(partStateReader1.getId())),\n                    Component.translatable(L10NValues.VARIABLE_ERROR_PARTNOTINNETWORK, Integer.toString(partStateReader2.getId()))\n            ), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplayVariableStoreDisconnected(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north().north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north().north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableAdded);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(partAndState.getRight().isDeactivated(), Component.literal(\"Display panel is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.DISPLAY_PANEL.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ERROR,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), null);\n            helper.assertValueEqual(partAndState.getRight().getGlobalErrors(), Lists.newArrayList(\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect1).getId())),\n                    Component.translatable(L10NValues.OPERATOR_ERROR_VARIABLENOTINNETWORK, Integer.toString(getVariableFacade(helper.getLevel(), variableAspect2).getId()))\n            ), Component.literal(\"Display panel errors do not match\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testCombinedAspectsRedstoneReaderAddOperatorToDisplayBecomesFullyConnected(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in writer\n        Pair<PartTypePanelDisplay, PartTypePanelDisplay.State> partAndState = placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east().east()), Direction.EAST), variableAdded);\n\n        helper.runAtTickTime(5, () -> helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value()));\n\n        helper.succeedOnTickWhen(6, () -> {\n            assertValueEqual(helper, partAndState.getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(29));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsDelayer.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDelay;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsDelayer {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testDelayerFromAddition(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place proxy\n        helper.setBlock(POS.below(), RegistryEntries.BLOCK_DELAY.value());\n        BlockEntityDelay delay = helper.getBlockEntity(POS.below(), BlockEntityDelay.class);\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in delay\n        delay.getInventory().setItem(0, variableAdded);\n        delay.getInventory().setItem(1, new ItemStack(RegistryEntries.ITEM_VARIABLE.get()));\n\n        // Wait a tick for the delay to write\n        Wrapper<Pair<PartTypePanelDisplay, PartTypePanelDisplay.State>> partAndState = new Wrapper<>(null);\n        helper.runAfterDelay(1, () -> {\n            ItemStack variableDelayed = delay.getInventory().getItem(2);\n\n            // Place delayed variable in writer\n            partAndState.set(placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableDelayed));\n        });\n\n        // Remove signal of the full redstone block after 3 ticks\n        helper.runAfterDelay(3, () -> helper.destroyBlock(POS.south()));\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(partAndState.get() != null, Component.literal(\"No variable was delayed\"));\n            assertValueEqual(helper, partAndState.get().getRight().getDisplayValue(), ValueTypeList.ValueList.ofAll(\n                    ValueTypeInteger.ValueInteger.of(29),\n                    ValueTypeInteger.ValueInteger.of(29),\n                    ValueTypeInteger.ValueInteger.of(29),\n                    ValueTypeInteger.ValueInteger.of(14)\n            ));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDryingBasin;\n\npublic class GameTestsDryingBasin {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200)\n    public void testDryingBasin(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_DRYING_BASIN.value());\n        BlockEntityDryingBasin machine = helper.getBlockEntity(POS, BlockEntityDryingBasin.class);\n\n        // Set input\n        machine.getTank().setFluid(new FluidStack(RegistryEntries.FLUID_MENRIL_RESIN.get(), 1_000));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), RegistryEntries.BLOCK_CRYSTALIZED_MENRIL_BLOCK.get().asItem(), Component.literal(\"Machine did not produce item output\"));\n            helper.assertTrue(machine.getTank().getFluid().isEmpty(), Component.literal(\"Machine did not consume fluid input\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200)\n    public void testDryingBasinNoFluidInput(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_DRYING_BASIN.value());\n        BlockEntityDryingBasin machine = helper.getBlockEntity(POS, BlockEntityDryingBasin.class);\n\n        // Set input: rotten flesh dries into leather (no fluid input required)\n        machine.getInventory().setItem(0, new ItemStack(Items.ROTTEN_FLESH));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), Items.LEATHER, Component.literal(\"Machine did not produce leather from rotten flesh\"));\n            helper.assertTrue(machine.getTank().getFluid().isEmpty(), Component.literal(\"Machine unexpectedly produced fluid output\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200)\n    public void testDryingBasinItemAndFluidInputs(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_DRYING_BASIN.value());\n        BlockEntityDryingBasin machine = helper.getBlockEntity(POS, BlockEntityDryingBasin.class);\n\n        // Set both item and fluid inputs simultaneously\n        machine.getInventory().setItem(0, new ItemStack(Items.ROTTEN_FLESH));\n        machine.getTank().setFluid(new FluidStack(RegistryEntries.FLUID_MENRIL_RESIN.get(), 1_000));\n\n        helper.succeedWhen(() -> {\n            // The item-only recipe (rotten_flesh -> leather) should run despite fluid being present.\n            // The fluid recipe cannot run afterwards because leather (the output) occupies slot 0.\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), Items.LEATHER, Component.literal(\"Machine did not process item-only recipe (rotten flesh -> leather)\"));\n            helper.assertValueEqual(machine.getTank().getFluid().getAmount(), 1_000, Component.literal(\"Menril resin should still be in tank (fluid recipe cannot run while leather is in slot 0)\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testDryingBasinPlaceSingleItem(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_DRYING_BASIN.value());\n        BlockEntityDryingBasin machine = helper.getBlockEntity(POS, BlockEntityDryingBasin.class);\n\n        // Player right-clicks empty basin with a single item\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.getInventory().setItem(player.getInventory().getSelectedSlot(), new ItemStack(Items.DIRT, 1));\n\n        BlockState blockState = helper.getLevel().getBlockState(helper.absolutePos(POS));\n        blockState.useWithoutItem(helper.getLevel(), player, new BlockHitResult(helper.absolutePos(POS).getCenter(), Direction.UP, helper.absolutePos(POS), false));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), Items.DIRT, Component.literal(\"Basin did not receive item\"));\n            helper.assertTrue(player.getInventory().getSelectedItem().isEmpty(), Component.literal(\"Player hand should be empty after placing single item\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testDryingBasinPlaceStackItem(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_DRYING_BASIN.value());\n        BlockEntityDryingBasin machine = helper.getBlockEntity(POS, BlockEntityDryingBasin.class);\n\n        // Player right-clicks empty basin with a stack of 5 items\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.getInventory().setItem(player.getInventory().getSelectedSlot(), new ItemStack(Items.DIRT, 5));\n\n        BlockState blockState = helper.getLevel().getBlockState(helper.absolutePos(POS));\n        blockState.useWithoutItem(helper.getLevel(), player, new BlockHitResult(helper.absolutePos(POS).getCenter(), Direction.UP, helper.absolutePos(POS), false));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), Items.DIRT, Component.literal(\"Basin did not receive item from stack\"));\n            helper.assertValueEqual(player.getInventory().getSelectedItem().getCount(), 4, Component.literal(\"Player stack should be decremented by 1 after placing item\"));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsFacades.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.Blocks;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\n\npublic class GameTestsFacades {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(1, 1, 1);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testFacadeCable(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place facade as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStackFacade = new ItemStack(RegistryEntries.ITEM_FACADE.value());\n        RegistryEntries.ITEM_FACADE.value().writeFacadeBlock(itemStackFacade, Blocks.COPPER_BLOCK.defaultBlockState());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackFacade);\n        helper.placeAt(player, itemStackFacade, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1 != null, Component.literal(\"Network is null\"));\n            helper.assertTrue(CableHelpers.hasFacade(helper.getLevel(), helper.absolutePos(POS), helper.getBlockState(POS)), Component.literal(\"Facade is not present\"));\n            helper.assertValueEqual(CableHelpers.getFacade(helper.getLevel(), helper.absolutePos(POS), helper.getBlockState(POS)).get(), Blocks.COPPER_BLOCK.defaultBlockState(), Component.literal(\"Facade type is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testFacadeCablesConnect(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place facade as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStackFacade = new ItemStack(RegistryEntries.ITEM_FACADE.value());\n        RegistryEntries.ITEM_FACADE.value().writeFacadeBlock(itemStackFacade, Blocks.COPPER_BLOCK.defaultBlockState());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackFacade);\n        helper.placeAt(player, itemStackFacade, POS.south(), Direction.NORTH);\n\n        // Place cables around\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.EAST, Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().east())),\n                    Sets.newHashSet(Direction.WEST, Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertTrue(CableHelpers.hasFacade(helper.getLevel(), helper.absolutePos(POS), helper.getBlockState(POS)), Component.literal(\"Facade is not present\"));\n            helper.assertValueEqual(CableHelpers.getFacade(helper.getLevel(), helper.absolutePos(POS), helper.getBlockState(POS)).get(), Blocks.COPPER_BLOCK.defaultBlockState(), Component.literal(\"Facade type is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testFacadeCableWithPart(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place facade as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStackFacade = new ItemStack(RegistryEntries.ITEM_FACADE.value());\n        RegistryEntries.ITEM_FACADE.value().writeFacadeBlock(itemStackFacade, Blocks.COPPER_BLOCK.defaultBlockState());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackFacade);\n        helper.placeAt(player, itemStackFacade, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1 != null, Component.literal(\"Network is null\"));\n            helper.assertTrue(CableHelpers.hasFacade(helper.getLevel(), helper.absolutePos(POS), helper.getBlockState(POS)), Component.literal(\"Facade is not present\"));\n            helper.assertValueEqual(CableHelpers.getFacade(helper.getLevel(), helper.absolutePos(POS), helper.getBlockState(POS)).get(), Blocks.COPPER_BLOCK.defaultBlockState(), Component.literal(\"Facade type is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testFacadeCableWithLever(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place facade as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStackFacade = new ItemStack(RegistryEntries.ITEM_FACADE.value());\n        RegistryEntries.ITEM_FACADE.value().writeFacadeBlock(itemStackFacade, Blocks.COPPER_BLOCK.defaultBlockState());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackFacade);\n        helper.placeAt(player, itemStackFacade, POS.south(), Direction.NORTH);\n\n        // Attempt to place lever as player\n        ItemStack itemStack = new ItemStack(Items.LEVER);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(player.getItemInHand(InteractionHand.MAIN_HAND).isEmpty(), Component.literal(\"Item hand is not empty\"));\n            helper.assertBlockPresent(Blocks.LEVER, POS.north());\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsFluids.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.BucketPickup;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Game tests for fluid block interaction (bucket pickup, block replacement).\n * @author rubensworks\n */\npublic class GameTestsFluids {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testMenrilResinFluidReplaceable(GameTestHelper helper) {\n        // Place menril resin fluid block\n        helper.setBlock(POS, RegistryEntries.BLOCK_FLUID_MENRIL_RESIN.value().defaultBlockState());\n        helper.assertTrue(\n                helper.getBlockState(POS).getBlock() == RegistryEntries.BLOCK_FLUID_MENRIL_RESIN.value(),\n                Component.literal(\"Menril resin fluid block was not placed\"));\n\n        // Place a solid block where the fluid is using a player with an ItemStack,\n        // which triggers the replaceable check\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack dirtStack = new ItemStack(Items.DIRT);\n        player.setItemInHand(InteractionHand.MAIN_HAND, dirtStack);\n        helper.placeAt(player, dirtStack, POS.south(), Direction.NORTH);\n\n        // Verify the fluid was replaced by the placed block\n        helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.DIRT, POS));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testMenrilResinFluidBucketPickup(GameTestHelper helper) {\n        // Place menril resin fluid block (source level = 0)\n        BlockState fluidState = RegistryEntries.BLOCK_FLUID_MENRIL_RESIN.value().defaultBlockState();\n        helper.setBlock(POS, fluidState);\n        helper.assertTrue(\n                helper.getBlockState(POS).getBlock() == RegistryEntries.BLOCK_FLUID_MENRIL_RESIN.value(),\n                Component.literal(\"Menril resin fluid block was not placed\"));\n\n        // Simulate bucket pickup using the BucketPickup interface\n        Level level = helper.getLevel();\n        BlockPos absPos = helper.absolutePos(POS);\n        BlockState state = level.getBlockState(absPos);\n        helper.assertTrue(\n                state.getBlock() instanceof BucketPickup,\n                Component.literal(\"Block does not implement BucketPickup\"));\n\n        ItemStack result = ((BucketPickup) state.getBlock()).pickupBlock(null, level, absPos, state);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(result.isEmpty(), Component.literal(\"Bucket pickup returned an empty ItemStack\"));\n            helper.assertTrue(\n                    result.getItem() == RegistryEntries.ITEM_BUCKET_MENRIL_RESIN.get(),\n                    Component.literal(\"Bucket pickup returned wrong item: \" + result));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testLiquidChorusFluidReplaceable(GameTestHelper helper) {\n        // Place liquid chorus fluid block\n        helper.setBlock(POS, RegistryEntries.BLOCK_FLUID_LIQUID_CHORUS.value().defaultBlockState());\n        helper.assertTrue(\n                helper.getBlockState(POS).getBlock() == RegistryEntries.BLOCK_FLUID_LIQUID_CHORUS.value(),\n                Component.literal(\"Liquid chorus fluid block was not placed\"));\n\n        // Place a solid block where the fluid is using a player with an ItemStack,\n        // which triggers the replaceable check\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack dirtStack = new ItemStack(Items.DIRT);\n        player.setItemInHand(InteractionHand.MAIN_HAND, dirtStack);\n        helper.placeAt(player, dirtStack, POS.south(), Direction.NORTH);\n\n        // Verify the fluid was replaced by the placed block\n        helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.DIRT, POS));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testLiquidChorusFluidBucketPickup(GameTestHelper helper) {\n        // Place liquid chorus fluid block (source level = 0)\n        BlockState fluidState = RegistryEntries.BLOCK_FLUID_LIQUID_CHORUS.value().defaultBlockState();\n        helper.setBlock(POS, fluidState);\n        helper.assertTrue(\n                helper.getBlockState(POS).getBlock() == RegistryEntries.BLOCK_FLUID_LIQUID_CHORUS.value(),\n                Component.literal(\"Liquid chorus fluid block was not placed\"));\n\n        // Simulate bucket pickup using the BucketPickup interface\n        Level level = helper.getLevel();\n        BlockPos absPos = helper.absolutePos(POS);\n        BlockState state = level.getBlockState(absPos);\n        helper.assertTrue(\n                state.getBlock() instanceof BucketPickup,\n                Component.literal(\"Block does not implement BucketPickup\"));\n\n        ItemStack result = ((BucketPickup) state.getBlock()).pickupBlock(null, level, absPos, state);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(result.isEmpty(), Component.literal(\"Bucket pickup returned an empty ItemStack\"));\n            helper.assertTrue(\n                    result.getItem() == RegistryEntries.ITEM_BUCKET_LIQUID_CHORUS.get(),\n                    Component.literal(\"Bucket pickup returned wrong item: \" + result));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsFuzzing.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Holder;\nimport net.minecraft.core.Registry;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.gametest.framework.*;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtIo;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;\nimport org.apache.logging.log4j.Level;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.gametest.fuzzing.FuzzingGameTestInstance;\nimport org.cyclops.integrateddynamics.gametest.fuzzing.NetworkFuzzer;\nimport org.cyclops.integrateddynamics.gametest.fuzzing.NetworkFuzzerException;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.function.BiConsumer;\n\n/**\n * Fuzz testing game tests for Integrated Dynamics networks.\n * Generates random networks and verifies they do not cause crashes.\n * Enabled via the FUZZING_ITERATIONS environment variable.\n * Crashed networks are saved as .nbt structure files in the fuzzing_crashes/ directory.\n * @author rubensworks\n */\npublic class GameTestsFuzzing {\n\n    public static final int MAX_NETWORK_SIZE = 100;\n    public static final int MAX_PARTS = 20;\n    public static final int MAX_OPERATORS = 50;\n    public static final int RUN_TICKS = 50;\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    // Start at (2,1,2) to leave room for context blocks on all sides within the 10x10x10 template\n    public static final BlockPos START_POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    // Cable grid dimensions: 5x5 per layer, fitting within the 10x10x10 template\n    private static final int CABLE_GRID_X = 5;\n    private static final int CABLE_GRID_Z = 5;\n\n    private static final String CRASHES_DIR = \"fuzzing_crashes\";\n\n    /**\n     * Get the number of fuzzing iterations from environment variable or system property.\n     * Returns 0 if not set (disabled).\n     */\n    private static int getFuzzingIterations() {\n        String envVar = System.getenv(\"FUZZING_ITERATIONS\");\n        if (envVar != null) {\n            return Integer.parseInt(envVar);\n        }\n        String sysProp = System.getProperty(\"FUZZING_ITERATIONS\");\n        return sysProp != null ? Integer.parseInt(sysProp) : 0;\n    }\n\n    public static void runFuzzingIteration(GameTestHelper helper, int index) {\n        long seed = new Random().nextLong();\n        Random random = new Random(seed);\n        int networkSize = random.nextInt(MAX_NETWORK_SIZE) + 1;\n        int numParts = random.nextInt(MAX_PARTS) + 1;\n        int maxOperatorDepth = random.nextInt(MAX_OPERATORS) + 1;\n\n        IntegratedDynamics.clog(Level.INFO, \"[Fuzzing] iteration=\" + index + \" seed=\" + seed\n                + \", networkSize=\" + networkSize\n                + \", numParts=\" + numParts + \", maxOperatorDepth=\" + maxOperatorDepth);\n\n        BlockPos absStartPos = helper.absolutePos(START_POS);\n\n        try {\n            generateFuzzedNetwork(helper, absStartPos, networkSize, numParts, maxOperatorDepth, random);\n        } catch (RuntimeException e) {\n            throw new GameTestAssertException(Component.literal(\"[Fuzzing] \" + e.getMessage()), (int) helper.getTick());\n        } catch (NetworkFuzzerException e) {\n            throw new GameTestAssertException(Component.literal(\"[Fuzzing] Fuzzed network generation threw exception: \" + e.getMessage()), (int) helper.getTick());\n        } finally {\n            // Always save the structure so it is available as a CI artifact for inspection.\n            String structureName = \"fuzz_\" + index + \"_\" + System.currentTimeMillis()\n                    + \"_n\" + networkSize + \"_p\" + numParts + \"_d\" + maxOperatorDepth;\n            try {\n                saveStructure(helper.getLevel(), absStartPos, networkSize, structureName);\n                IntegratedDynamics.clog(Level.INFO, \"[Fuzzing] Structure saved: \" + structureName + \".nbt\");\n            } catch (Exception e) {\n                IntegratedDynamics.clog(Level.WARN, \"[Fuzzing] Failed to save structure: \" + e);\n            }\n        }\n\n        // Let the network run for RUN_TICKS, then proceed to the next iteration\n        helper.runAfterDelay(RUN_TICKS, helper::succeed);\n    }\n\n    public static void registerCommonTests(String modId, BiConsumer<Identifier, GameTestInstance> registrar, Registry<TestEnvironmentDefinition<?>> testEnvironmentRegistry) {\n        for (FuzzingGameTestInstance testInstance : fuzzingTests(modId, testEnvironmentRegistry)) {\n            registrar.accept(testInstance.getId(), testInstance);\n        }\n    }\n\n    public static Collection<FuzzingGameTestInstance> fuzzingTests(String modId, Registry<TestEnvironmentDefinition<?>> testEnvironmentRegistry) {\n        List<FuzzingGameTestInstance> testsList = Lists.newArrayList();\n\n        int fuzzingIterations = getFuzzingIterations();\n        if (fuzzingIterations == 0) {\n            IntegratedDynamics.clog(Level.INFO, \"[Fuzzing] Disabled (FUZZING_ITERATIONS not set to a number)\");\n        }\n\n        Holder.Reference<TestEnvironmentDefinition<?>> environment = testEnvironmentRegistry.getOrThrow(ResourceKey.create(\n                Registries.TEST_ENVIRONMENT,\n                Identifier.parse(\"default\")\n        ));\n        for (int i = 0; i < fuzzingIterations; i++) {\n            testsList.add(new FuzzingGameTestInstance(\n                    new TestData<Holder<TestEnvironmentDefinition<?>>>(\n                            environment,\n                            Identifier.parse(\"integrateddynamics:test\"),\n                            RUN_TICKS + 10,\n                            1,\n                            true,\n                            Rotation.NONE\n                    ),\n                    i));\n        }\n\n        return testsList;\n    }\n\n    /**\n     * Clear blocks placed during a fuzzing iteration so the next iteration starts clean.\n     */\n    private static void clearArea(GameTestHelper helper, BlockPos absStartPos, int cableCount) {\n        int layers = Math.max(1, (cableCount + CABLE_GRID_X * CABLE_GRID_Z - 1) / (CABLE_GRID_X * CABLE_GRID_Z));\n        for (int x = -1; x < CABLE_GRID_X + 3; x++) {\n            for (int y = -1; y < layers + 2; y++) {\n                for (int z = -1; z < CABLE_GRID_Z + 2; z++) {\n                    BlockPos pos = absStartPos.offset(x, y, z);\n                    if (!helper.getLevel().isEmptyBlock(pos)) {\n                        helper.getLevel().removeBlock(pos, false);\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Generate a fuzzed network using NetworkFuzzer.\n     */\n    private static void generateFuzzedNetwork(GameTestHelper helper, BlockPos startPos,\n                                               int cableCount, int numParts, int maxOperatorDepth, Random random) throws NetworkFuzzerException {\n        net.minecraft.server.level.ServerLevel level = helper.getLevel();\n\n        // Place cables in a compact grid (CABLE_GRID_X x CABLE_GRID_Z per layer)\n        List<BlockPos> cables = new ArrayList<>();\n        for (int i = 0; i < cableCount; i++) {\n            int x = i % CABLE_GRID_X;\n            int z = (i / CABLE_GRID_X) % CABLE_GRID_Z;\n            int y = i / (CABLE_GRID_X * CABLE_GRID_Z);\n            BlockPos pos = startPos.offset(x, y, z);\n            level.setBlock(pos, RegistryEntries.BLOCK_CABLE.value().defaultBlockState(), 2);\n            cables.add(pos);\n        }\n\n        if (cables.isEmpty()) {\n            return;\n        }\n\n        // Place a variable store for intermediate variables\n        BlockPos varStorePos = startPos.offset(CABLE_GRID_X + 1, 0, 0);\n        level.setBlock(varStorePos, RegistryEntries.BLOCK_VARIABLE_STORE.get().defaultBlockState(), 2);\n\n        // Connect the variable store to the cable grid with a cable\n        BlockPos cableToVarStore = startPos.offset(CABLE_GRID_X, 0, 0);\n        level.setBlock(cableToVarStore, RegistryEntries.BLOCK_CABLE.value().defaultBlockState(), 2);\n\n        BlockEntityVariablestore varStore = (BlockEntityVariablestore) level.getBlockEntity(varStorePos);\n        if (varStore == null) {\n            throw new NetworkFuzzerException(\"[Fuzzing] Failed to create variable store at \" + varStorePos);\n        }\n\n        // Use NetworkFuzzer to generate the network\n        NetworkFuzzer fuzzer = new NetworkFuzzer(helper, random, maxOperatorDepth, cables, varStore, level, startPos);\n\n        // Generate multiple random connections based on numParts\n        for (int i = 0; i < numParts; i++) {\n            if (!fuzzer.generate()) {\n                break;\n            }\n        }\n    }\n\n    /**\n     * Save the current network region as a Minecraft structure (.nbt) file.\n     */\n    private static void saveStructure(net.minecraft.server.level.ServerLevel level, BlockPos startPos,\n                                       int cableCount, String name) throws IOException {\n        int layers = Math.max(1, (cableCount + CABLE_GRID_X * CABLE_GRID_Z - 1) / (CABLE_GRID_X * CABLE_GRID_Z));\n        Vec3i size = new Vec3i(CABLE_GRID_X + 3, layers + 2, CABLE_GRID_Z + 2);\n\n        StructureTemplate template = new StructureTemplate();\n        BlockPos captureFrom = startPos.offset(-1, -1, -1);\n        template.fillFromWorld(level, captureFrom, size, true, List.of());\n\n        CompoundTag tag = template.save(new CompoundTag());\n        Path crashDir = Paths.get(CRASHES_DIR);\n        Files.createDirectories(crashDir);\n        NbtIo.writeCompressed(tag, crashDir.resolve(name + \".nbt\"));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsGenerator.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityCoalGenerator;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\n\npublic class GameTestsGenerator {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testGenerator(GameTestHelper helper) {\n        // Place generator\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        BlockEntityCoalGenerator generator = helper.getBlockEntity(POS, BlockEntityCoalGenerator.class);\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.north(), BlockEntityEnergyBattery.class);\n\n        // Fill generator\n        generator.getInventory().setItem(0, new ItemStack(Items.COAL));\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(generator.getInventory().getItem(0).isEmpty(), Component.literal(\"Generator did not consume input\"));\n            helper.assertTrue(generator.isBurning(), Component.literal(\"Generator is not burning\"));\n            helper.assertTrue(battery.getEnergyStored() > 1000, Component.literal(\"Energy in battery did not increase\"));\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsMaterializer.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMaterializer;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsMaterializer {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testMaterializerFromAddition(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place materializer\n        helper.setBlock(POS.below(), RegistryEntries.BLOCK_MATERIALIZER.value());\n        BlockEntityMaterializer materializer = helper.getBlockEntity(POS.below(), BlockEntityMaterializer.class);\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in materializer\n        materializer.getInventory().setItem(0, variableAdded);\n        materializer.getInventory().setItem(1, new ItemStack(RegistryEntries.ITEM_VARIABLE.get()));\n\n        // Wait a tick for the materializer to write\n        Wrapper<Pair<PartTypePanelDisplay, PartTypePanelDisplay.State>> partAndState = new Wrapper<>(null);\n        helper.runAfterDelay(1, () -> {\n            ItemStack variableMaterialized = materializer.getInventory().getItem(2);\n\n            // Place materialized variable in writer\n            partAndState.set(placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableMaterialized));\n        });\n\n        // Wait a while to remove the variable store\n        helper.runAfterDelay(2, () -> helper.destroyBlock(POS.north()));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.get(), POS.north());\n            helper.assertTrue(partAndState.get() != null, Component.literal(\"No variable was materialized\"));\n            assertValueEqual(helper, partAndState.get().getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(29));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalDryingBasin;\n\npublic class GameTestsMechanicalDryingBasin {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testMechanicalDryingBasin(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_MECHANICAL_DRYING_BASIN.value());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        BlockEntityMechanicalDryingBasin machine = helper.getBlockEntity(POS, BlockEntityMechanicalDryingBasin.class);\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.north(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100_000);\n\n        // Set input\n        machine.getTankInput().setFluid(new FluidStack(RegistryEntries.FLUID_MENRIL_RESIN.get(), 1_000));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(1).getItem(), RegistryEntries.BLOCK_CRYSTALIZED_MENRIL_BLOCK.get().asItem(), Component.literal(\"Machine did not produce item output\"));\n            helper.assertTrue(machine.getTankInput().getFluid().isEmpty(), Component.literal(\"Machine did not consume fluid input\"));\n            helper.assertTrue(battery.getEnergyStored() < 100_000, Component.literal(\"Energy in battery did not decrease\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = 200)\n    public void testMechanicalDryingBasinItemAndFluidInputs(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_MECHANICAL_DRYING_BASIN.value());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        BlockEntityMechanicalDryingBasin machine = helper.getBlockEntity(POS, BlockEntityMechanicalDryingBasin.class);\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.north(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100_000);\n\n        // Set both item and fluid inputs simultaneously\n        machine.getInventory().setItem(0, new ItemStack(Items.ROTTEN_FLESH));\n        machine.getTankInput().setFluid(new FluidStack(RegistryEntries.FLUID_MENRIL_RESIN.get(), 1_000));\n\n        helper.succeedWhen(() -> {\n            // Both item-only (rotten_flesh -> leather) and fluid-only (menril_resin -> crystalized_menril_block)\n            // recipes should have been processed sequentially\n            boolean hasLeather = false;\n            boolean hasMenrilBlock = false;\n            for (int i = 1; i <= 4; i++) {\n                if (machine.getInventory().getItem(i).is(Items.LEATHER)) {\n                    hasLeather = true;\n                }\n                if (machine.getInventory().getItem(i).is(RegistryEntries.BLOCK_CRYSTALIZED_MENRIL_BLOCK.get().asItem())) {\n                    hasMenrilBlock = true;\n                }\n            }\n            helper.assertTrue(hasLeather, Component.literal(\"Machine did not process item-only recipe (rotten flesh -> leather)\"));\n            helper.assertTrue(hasMenrilBlock, Component.literal(\"Machine did not process fluid-only recipe (menril resin -> crystalized menril block)\"));\n            helper.assertTrue(machine.getInventory().getItem(0).isEmpty(), Component.literal(\"Machine did not consume item input\"));\n            helper.assertTrue(machine.getTankInput().getFluid().isEmpty(), Component.literal(\"Machine did not consume fluid input\"));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalSqueezer;\n\npublic class GameTestsMechanicalSqueezer {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testMechanicalSqueezer(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_MECHANICAL_SQUEEZER.value());\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        BlockEntityMechanicalSqueezer machine = helper.getBlockEntity(POS, BlockEntityMechanicalSqueezer.class);\n        BlockEntityEnergyBattery battery = helper.getBlockEntity(POS.north(), BlockEntityEnergyBattery.class);\n        battery.setEnergyStored(100_000);\n\n        // Set input\n        machine.getInventory().setItem(0, new ItemStack(RegistryEntries.BLOCK_MENRIL_LOG.get()));\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(machine.getInventory().getItem(0).isEmpty(), Component.literal(\"Machine did not consume input\"));\n            helper.assertValueEqual(machine.getInventory().getItem(1).getItem(), RegistryEntries.ITEM_CRYSTALIZED_MENRIL_CHUNK.get(), Component.literal(\"Machine did not produce item output\"));\n            helper.assertValueEqual(machine.getTank().getFluid().getFluid(), RegistryEntries.FLUID_MENRIL_RESIN.get(), Component.literal(\"Machine did not produce fluid output\"));\n            helper.assertTrue(battery.getEnergyStored() < 100_000, Component.literal(\"Energy in battery did not decrease\"));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsNetwork.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.client.particle.ParticleBlurData;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\n\nimport java.util.Optional;\n\npublic class GameTestsNetwork {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(1, 1, 1);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkSingle(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.EAST, Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().east())),\n                    Sets.newHashSet(Direction.WEST, Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkSingleByPlayer(GameTestHelper helper) {\n        // Place cables as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.BLOCK_CABLE.value(), 4);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n\n        helper.placeAt(player, itemStack, POS.south(), Direction.NORTH);\n        helper.placeAt(player, itemStack, POS.south().south(), Direction.NORTH);\n        helper.placeAt(player, itemStack, POS.south().east(), Direction.NORTH);\n        helper.placeAt(player, itemStack, POS.south().south().east(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.EAST, Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().east())),\n                    Sets.newHashSet(Direction.WEST, Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwo(GameTestHelper helper) {\n        // Player two networks with empty space inbetween\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(4, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(4, 0, 0)), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of disconnected cables are equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(4, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwoDisconnectedByWrench(GameTestHelper helper) {\n        // Place two networks directly next to each other\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(2, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.offset(1, 0, 0)).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS.offset(1, 0, 0)).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.offset(1, 0, 0)).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.offset(1, 0, 0)),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of wrench-disconnected cables are equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0))),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwoDisconnectedByWrenchOffhand(GameTestHelper helper) {\n        // Place two networks directly next to each other\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(2, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.OFF_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.offset(1, 0, 0)).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS.offset(1, 0, 0)).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.OFF_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.offset(1, 0, 0)).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.offset(1, 0, 0)),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of wrench-disconnected cables are equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0))),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwoDisconnectedAndReconnectedByWrench(GameTestHelper helper) {\n        // Place two networks directly next to each other\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(2, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.offset(1, 0, 0)).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS.offset(1, 0, 0)).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.offset(1, 0, 0)).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.offset(1, 0, 0)),\n                        false)\n        );\n\n        // And reconnect them using the wrench\n        player.setPos(helper.absolutePos(POS.offset(1, 0, 0)).getCenter().add(0.25, -1.5, 0));\n        player.setYRot(90);\n        helper.getLevel().sendParticles(new ParticleBlurData(1, 1, 1, 1, 100), player.position().x, player.position().y + player.getEyeHeight(), player.position().z, 10, 0, 0, 0, 0); // TODO: for debugging\n        helper.getBlockState(POS.offset(1, 0, 0)).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.offset(1, 0, 0)).getCenter(),\n                        Direction.EAST,\n                        helper.absolutePos(POS.offset(1, 0, 0)),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of wrench-reconnected cables are equal\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(Direction.WEST, Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0))),\n                    Sets.newHashSet(Direction.WEST, Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwoRemoved(GameTestHelper helper) {\n        // Place two networks directly next to each other\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(2, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        // And remove one cable\n        helper.destroyBlock(POS.offset(1, 0, 0));\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            Optional<INetwork> network2 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            helper.assertTrue(network1 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2.isEmpty(), Component.literal(\"Network of removed cable is not empty\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0))),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwoRemovedByWrench(GameTestHelper helper) {\n        // Place two networks directly next to each other\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(2, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        // And remove one cable using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove cable!\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.offset(1, 0, 0)).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS.offset(1, 0, 0)).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.offset(1, 0, 0)).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.offset(1, 0, 0)),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            Optional<INetwork> network2 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            helper.assertTrue(network1 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2.isEmpty(), Component.literal(\"Network of removed cable is not empty\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0))),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.ITEM_CABLE.get());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkTwoRemovedByWrenchOffand(GameTestHelper helper) {\n        // Place two networks directly next to each other\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(1, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(2, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.offset(3, 0, 0), RegistryEntries.BLOCK_CABLE.value());\n\n        // And remove one cable using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove cable!\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.OFF_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.offset(1, 0, 0)).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS.offset(1, 0, 0)).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.OFF_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.offset(1, 0, 0)).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.offset(1, 0, 0)),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            Optional<INetwork> network2 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0)), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0)), null);\n            INetwork network4 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0)), null);\n            helper.assertTrue(network1 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2.isEmpty(), Component.literal(\"Network of removed cable is not empty\"));\n            helper.assertTrue(network3 == network4, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(1, 0, 0))),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(2, 0, 0))),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.offset(3, 0, 0))),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.ITEM_CABLE.get());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCableWithLever(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS.offset(1, 0, 1), RegistryEntries.BLOCK_CABLE.value());\n\n        // Attempt to place lever as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(Items.LEVER);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.offset(1, 0, 1).south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(player.getItemInHand(InteractionHand.MAIN_HAND).getItem(), Items.LEVER, Component.literal(\"Item hand is incorrect\"));\n            helper.assertBlockNotPresent(Blocks.LEVER, POS.offset(1, 0, 1).north());\n        });\n    }\n\n    /* ----- Variable store ----- */\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkVariableStoreSingle(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_VARIABLE_STORE.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkVariableStoreSingleAfterwards(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place variable store afterwards\n        helper.setBlock(POS, RegistryEntries.BLOCK_VARIABLE_STORE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkVariableStoreDisconnectByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_VARIABLE_STORE.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.east()).getCenter().add(-0.25, -1.5, -0.5));\n        helper.getBlockState(POS.east()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.east()).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.east()),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkVariableStoreBreakByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_VARIABLE_STORE.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove store!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_VARIABLE_STORE.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkVariableStoreBreakByPickaxe(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_VARIABLE_STORE.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkVariableStoreBreakByTnt(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_VARIABLE_STORE.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_VARIABLE_STORE.get().asItem());\n        });\n    }\n\n    /* ----- Coal Generator ----- */\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCoalGeneratorSingle(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCoalGeneratorSingleAfterwards(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place variable store afterwards\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCoalGeneratorDisconnectByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.east()).getCenter().add(-0.25, -1.5, -0.5));\n        helper.getBlockState(POS.east()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.east()).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.east()),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCoalGeneratorBreakByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove store!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_COAL_GENERATOR.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCoalGeneratorBreakByPickaxe(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkCoalGeneratorBreakByTnt(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_COAL_GENERATOR.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_COAL_GENERATOR.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_COAL_GENERATOR.get().asItem());\n        });\n    }\n\n    /* ----- Energy Battery ----- */\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkEnergyBatterySingle(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkEnergyBatterySingleAfterwards(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place variable store afterwards\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkEnergyBatteryDisconnectByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.east()).getCenter().add(-0.25, -1.5, -0.5));\n        helper.getBlockState(POS.east()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.east()).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.east()),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkEnergyBatteryBreakByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove store!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).useItemOn(new ItemStack(RegistryEntries.ITEM_WRENCH.value()), helper.getLevel(), player, InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_ENERGY_BATTERY.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_ENERGY_BATTERY.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkEnergyBatteryBreakByPickaxe(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkEnergyBatteryBreakByTnt(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_ENERGY_BATTERY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_ENERGY_BATTERY.get().asItem());\n        });\n    }\n\n    /* ----- Delayer ----- */\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkDelayerSingle(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_DELAY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkDelayerSingleAfterwards(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place variable store afterwards\n        helper.setBlock(POS, RegistryEntries.BLOCK_DELAY.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkDelayerDisconnectByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_DELAY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.east()).getCenter().add(-0.25, -1.5, -0.5));\n        helper.getBlockState(POS.east()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.east()).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.east()),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkDelayerBreakByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_DELAY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove store!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_DELAY.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_DELAY.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkDelayerBreakByPickaxe(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_DELAY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_DELAY.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkDelayerBreakByTnt(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_DELAY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_VARIABLE_STORE.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_DELAY.get().asItem());\n        });\n    }\n\n    /* ----- Materializer ----- */\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMaterializerSingle(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_MATERIALIZER.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMaterializerSingleAfterwards(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place variable store afterwards\n        helper.setBlock(POS, RegistryEntries.BLOCK_MATERIALIZER.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMaterializerDisconnectByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_MATERIALIZER.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.east()).getCenter().add(-0.25, -1.5, -0.5));\n        helper.getBlockState(POS.east()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.east()).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.east()),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMaterializerBreakByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_MATERIALIZER.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove store!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_MATERIALIZER.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_MATERIALIZER.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMaterializerBreakByPickaxe(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_MATERIALIZER.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_MATERIALIZER.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMaterializerBreakByTnt(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_MATERIALIZER.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_MATERIALIZER.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_MATERIALIZER.get().asItem());\n        });\n    }\n\n    /* ----- Proxy ----- */\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkProxySingle(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_PROXY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkProxySingleAfterwards(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place variable store afterwards\n        helper.setBlock(POS, RegistryEntries.BLOCK_PROXY.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 == network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.EAST, Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkProxyDisconnectByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_PROXY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // And disconnect them using the wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        player.setPos(helper.absolutePos(POS.east()).getCenter().add(-0.25, -1.5, -0.5));\n        helper.getBlockState(POS.east()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS.east()).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS.east()),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(Direction.SOUTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(Direction.NORTH),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkProxyBreakByWrench(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_PROXY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove store!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_PROXY.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_PROXY.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkProxyBreakByPickaxe(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_PROXY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Break variable store with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter());\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_PROXY.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south()), null);\n            INetwork network3 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.east()), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n            helper.assertTrue(network2 != network3, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.east())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkProxyBreakByTnt(GameTestHelper helper) {\n        // Place variable store and cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_PROXY.value());\n        helper.setBlock(POS.south(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_PROXY.value(), POS);\n\n            Optional<INetwork> network1 = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null);\n            helper.assertTrue(network1.isEmpty(), Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            helper.assertItemEntityPresent(RegistryEntries.BLOCK_PROXY.get().asItem());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMonodirectional(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south().south().south(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place monodirectional connectors\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.CONNECTOR_MONO, new ItemStack(PartTypes.CONNECTOR_MONO.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.south().south().south()), Direction.NORTH, PartTypes.CONNECTOR_MONO, new ItemStack(PartTypes.CONNECTOR_MONO.getItem()));\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().south().south()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().south().south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkMonodirectionalUnconnected(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south().south().south().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place monodirectional connectors\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.CONNECTOR_MONO, new ItemStack(PartTypes.CONNECTOR_MONO.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.south().south().south().east()), Direction.NORTH, PartTypes.CONNECTOR_MONO, new ItemStack(PartTypes.CONNECTOR_MONO.getItem()));\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().south().south().east()), null);\n            helper.assertFalse(network1 == network2, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().south().south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkOmnidirectional(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south().south().south().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place omnidirectional connectors\n        ItemStack omniDirectionalConnector = new ItemStack(PartTypes.CONNECTOR_OMNI.getItem());\n        omniDirectionalConnector.set(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP, 1);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.CONNECTOR_OMNI, omniDirectionalConnector.copy());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.south().south().south().east()), Direction.NORTH, PartTypes.CONNECTOR_OMNI, omniDirectionalConnector.copy());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().south().south().east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().south().south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testNetworkOmnidirectionalUnconnected(GameTestHelper helper) {\n        // Place cables directly\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.south().south().south().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place omnidirectional connectors\n        ItemStack omniDirectionalConnector1 = new ItemStack(PartTypes.CONNECTOR_OMNI.getItem());\n        omniDirectionalConnector1.set(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP, 1);\n        ItemStack omniDirectionalConnector2 = new ItemStack(PartTypes.CONNECTOR_OMNI.getItem());\n        omniDirectionalConnector2.set(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP, 2);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.CONNECTOR_OMNI, omniDirectionalConnector1.copy());\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.south().south().south().east()), Direction.NORTH, PartTypes.CONNECTOR_OMNI, omniDirectionalConnector2.copy());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.south().south().south().east()), null);\n            helper.assertFalse(network1 == network2, Component.literal(\"Networks of connected cables are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.south().south().south())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsOffsets.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.RedStoneWireBlock;\nimport net.minecraft.world.level.block.state.properties.RedstoneSide;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.createVariableFromReader;\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.placeVariableInWriter;\n\npublic class GameTestsOffsets {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(4, 0, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsRedstoneReaderToWriter(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        increaseMaxOffset(helper, posReader, 4);\n        boolean changedOffsetReader = setOffset(posReader, new Vec3i(-2, 0, 0));\n\n        // Place redstone writer\n        PartPos posWriter = PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        increaseMaxOffset(helper, posWriter, 4);\n        boolean changedOffsetWriter = setOffset(posWriter, new Vec3i(2, 0, 0));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.offset(-2, 0, 0).west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.offset(2, 0, 0).east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(changedOffsetReader, Component.literal(\"Setting offset in reader failed\"));\n            helper.assertTrue(changedOffsetWriter, Component.literal(\"Setting offset in writer failed\"));\n\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.POWER, 14);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)).getState();\n            helper.assertFalse(partStateWriter.isDeactivated(), Component.literal(\"Writer is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertValueEqual(partStateWriter.getActiveAspect(), Aspects.Write.Redstone.INTEGER, Component.literal(\"Active aspect is incorrect\"));\n            helper.assertTrue(partStateWriter.getErrors(Aspects.Write.Redstone.INTEGER).isEmpty(), Component.literal(\"Active aspect has errors\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsRedstoneReaderToWriterOffset(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        increaseMaxOffset(helper, posReader, 4);\n        boolean changedOffsetReader = setOffset(posReader, new Vec3i(-2, 0, 0));\n\n        // Place redstone writer\n        PartPos posWriter = PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        increaseMaxOffset(helper, posWriter, 4);\n        boolean changedOffsetWriter = setOffset(posWriter, new Vec3i(2, 0, 0));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.offset(-2, 0, 0).west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.offset(2, 0, 0).east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(changedOffsetReader, Component.literal(\"Setting offset in reader failed\"));\n            helper.assertTrue(changedOffsetWriter, Component.literal(\"Setting offset in writer failed\"));\n\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.POWER, 14);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)).getState();\n            helper.assertFalse(partStateWriter.isDeactivated(), Component.literal(\"Writer is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertValueEqual(partStateWriter.getActiveAspect(), Aspects.Write.Redstone.INTEGER, Component.literal(\"Active aspect is incorrect\"));\n            helper.assertTrue(partStateWriter.getErrors(Aspects.Write.Redstone.INTEGER).isEmpty(), Component.literal(\"Active aspect has errors\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsRedstoneReaderToWriterOffsetAndSide(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        increaseMaxOffset(helper, posReader, 4);\n        boolean changedOffsetReader = setOffset(posReader, new Vec3i(-2, 0, 0));\n        setOffsetSide(posReader, Direction.SOUTH);\n\n        // Place redstone writer\n        PartPos posWriter = PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        increaseMaxOffset(helper, posWriter, 4);\n        boolean changedOffsetWriter = setOffset(posWriter, new Vec3i(2, 0, 0));\n        setOffsetSide(posWriter, Direction.SOUTH);\n\n        // Produce a redstone signal\n        helper.setBlock(POS.offset(-2, 0, 0).west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.offset(2, 0, 0).east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(changedOffsetReader, Component.literal(\"Setting offset in reader failed\"));\n            helper.assertTrue(changedOffsetWriter, Component.literal(\"Setting offset in writer failed\"));\n\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.POWER, 14);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)).getState();\n            helper.assertFalse(partStateWriter.isDeactivated(), Component.literal(\"Writer is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertValueEqual(partStateWriter.getActiveAspect(), Aspects.Write.Redstone.INTEGER, Component.literal(\"Active aspect is incorrect\"));\n            helper.assertTrue(partStateWriter.getErrors(Aspects.Write.Redstone.INTEGER).isEmpty(), Component.literal(\"Active aspect has errors\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsRedstoneReaderToWriterOnlySide(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        increaseMaxOffset(helper, posReader, 4);\n        setOffsetSide(posReader, Direction.SOUTH);\n\n        // Place redstone writer\n        PartPos posWriter = PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        increaseMaxOffset(helper, posWriter, 4);\n        setOffsetSide(posWriter, Direction.SOUTH);\n\n        // Produce a redstone signal\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertBlockProperty(POS.east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.east().east(), RedStoneWireBlock.POWER, 14);\n\n            IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)).getState();\n            helper.assertFalse(partStateWriter.isDeactivated(), Component.literal(\"Writer is deactivated\"));\n            helper.assertValueEqual(\n                    PartTypes.REDSTONE_WRITER.getBlockState(PartHelpers.getPartContainerChecked(PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST)), Direction.EAST).getValue(IgnoredBlockStatus.STATUS),\n                    IgnoredBlockStatus.Status.ACTIVE,\n                    Component.literal(\"Block status is incorrect\")\n            );\n            helper.assertValueEqual(partStateWriter.getActiveAspect(), Aspects.Write.Redstone.INTEGER, Component.literal(\"Active aspect is incorrect\"));\n            helper.assertTrue(partStateWriter.getErrors(Aspects.Write.Redstone.INTEGER).isEmpty(), Component.literal(\"Active aspect has errors\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsRedstoneReaderToWriterNoMaxOffset(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        boolean changedOffsetReader = setOffset(posReader, new Vec3i(-2, 0, 0));\n\n        // Place redstone writer\n        PartPos posWriter = PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        boolean changedOffsetWriter = setOffset(posWriter, new Vec3i(2, 0, 0));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.offset(-2, 0, 0).west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.offset(2, 0, 0).east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(changedOffsetReader, Component.literal(\"Setting offset in reader did not fail\"));\n            helper.assertFalse(changedOffsetWriter, Component.literal(\"Setting offset in writer did not fail\"));\n\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.POWER, 0);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsRedstoneReaderToWriterInsufficientMaxOffset(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        increaseMaxOffset(helper, posReader, 1);\n        boolean changedOffsetReader = setOffset(posReader, new Vec3i(-2, 0, 0));\n\n        // Place redstone writer\n        PartPos posWriter = PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.REDSTONE_WRITER, new ItemStack(PartTypes.REDSTONE_WRITER.getItem()));\n        increaseMaxOffset(helper, posWriter, 1);\n        boolean changedOffsetWriter = setOffset(posWriter, new Vec3i(2, 0, 0));\n\n        // Produce a redstone signal\n        helper.setBlock(POS.offset(-2, 0, 0).west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(-2, 0, 0).west().west().west(), Blocks.REDSTONE_TORCH);\n\n        // Writer redstone signal from redstone reader to variable card\n        ItemStack variableAspect = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Place variable in writer\n        placeVariableInWriter(helper, helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), Aspects.Write.Redstone.INTEGER, variableAspect);\n\n        // Place redstone wire next to redstone writer\n        helper.setBlock(POS.offset(2, 0, 0).east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east().below(), Blocks.STONE);\n        helper.setBlock(POS.offset(2, 0, 0).east().east().east(), Blocks.REDSTONE_WIRE);\n\n        helper.succeedWhen(() -> {\n            helper.assertFalse(changedOffsetReader, Component.literal(\"Setting offset in reader did not fail\"));\n            helper.assertFalse(changedOffsetWriter, Component.literal(\"Setting offset in writer did not fail\"));\n\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.WEST, RedstoneSide.SIDE);\n            helper.assertBlockProperty(POS.offset(2, 0, 0).east().east(), RedStoneWireBlock.POWER, 0);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsDropOnBreak(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS.above(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.above().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS.above()), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.above()), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        increaseMaxOffset(helper, posReader, 4);\n        boolean changedOffsetReader = setOffset(posReader, new Vec3i(-2, 0, 0));\n\n        // Remove redstone reader with pickaxe\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove part!\n        facePlayerToPart(player, posReader);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        helper.getBlockState(POS.above()).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS.above()), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS.above())));\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(changedOffsetReader, Component.literal(\"Setting offset in reader failed\"));\n\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertItemEntityPresent(RegistryEntries.ITEM_ENHANCEMENT_OFFSET.value());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testOffsetsNoDropOnBreakWithoutOffsets(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS.above(), RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.above().east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartPos posReader = PartPos.of(helper.getLevel(), helper.absolutePos(POS.above()), Direction.WEST);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.above()), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Remove redstone reader with pickaxe\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove part!\n        facePlayerToPart(player, posReader);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        helper.getBlockState(POS.above()).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS.above()), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS.above())));\n\n        helper.succeedWhen(() -> {\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_ENHANCEMENT_OFFSET.value());\n        });\n    }\n\n    public static void increaseMaxOffset(GameTestHelper helper, PartPos partPos, int offset) {\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_ENHANCEMENT_OFFSET.value());\n        RegistryEntries.ITEM_ENHANCEMENT_OFFSET.value().setEnhancementValue(itemStack, offset);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        facePlayerToPart(player, partPos);\n\n        partPos.getPos().getLevel(true).getBlockState(partPos.getPos().getBlockPos()).useItemOn(itemStack, helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        partPos.getPos().getBlockPos().getCenter(),\n                        partPos.getSide(),\n                        partPos.getPos().getBlockPos(),\n                        false));\n    }\n\n    public static void facePlayerToPart(Player player, PartPos partPos) {\n        player.setYRot(partPos.getSide().getRotation().y() * 180);\n        player.setPos(partPos.getPos().getBlockPos().getCenter()\n                .add(0, -1.5, 0)\n                .add(Vec3.atLowerCornerOf(partPos.getSide().getUnitVec3i()).multiply(0.75, 0.75, 0.75))\n        );\n\n//        helper.getLevel().sendParticles(new ParticleBlurData(1, 1, 1, 1, 100), player.position().x, player.position().y + player.getEyeHeight(), player.position().z, 10, 0, 0, 0, 0); // For debugging\n    }\n\n    public static boolean setOffset(PartPos partPos, Vec3i offset) {\n        PartHelpers.PartStateHolder<?, ?> partAndState = PartHelpers.getPart(partPos);\n        return ((IPartType) partAndState.getPart()).setTargetOffset(partAndState.getState(), partPos, offset);\n    }\n\n    public static void setOffsetSide(PartPos partPos, Direction direction) {\n        PartHelpers.PartStateHolder<?, ?> partAndState = PartHelpers.getPart(partPos);\n        ((IPartType) partAndState.getPart()).setTargetSideOverride(partAndState.getState(), direction);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsParts.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPathElement;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.part.PartTypeRedstoneReader;\n\npublic class GameTestsParts {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(1, 1, 1);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCable(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS), null), Component.literal(\"Cable is fake\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableAsPlayer(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS), null), Component.literal(\"Cable is fake\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableAsPlayer(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.SOUTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(!CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS.north()), null), Component.literal(\"Cable is not fake\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableRemove(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Remove cable and redstone reader\n        helper.destroyBlock(POS);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Part was not removed\"));\n\n            helper.assertItemEntityNotPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableRemove(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.north().north(), Direction.SOUTH);\n\n        // Remove cable and redstone reader\n        helper.destroyBlock(POS);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Part was not removed\"));\n\n            helper.assertItemEntityNotPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableRemoveWrench(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Remove redstone reader as player with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove part!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Part was not removed\"));\n\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertTrue(helper.getEntities(EntityType.ITEM).get(0).getItem().has(RegistryEntries.DATACOMPONENT_PART_STATE), Component.literal(\"Dropped part must have a state\"));\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableRemoveWrench(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.north().north(), Direction.SOUTH);\n\n        // Remove redstone reader as player with wrench\n        player.setShiftKeyDown(true); // To remove part!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.NORTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Part was not removed\"));\n\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertTrue(helper.getEntities(EntityType.ITEM).get(0).getItem().has(RegistryEntries.DATACOMPONENT_PART_STATE), Component.literal(\"Dropped part must have a state\"));\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableRemovePickaxe(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Remove redstone reader as player with pickaxe\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Part was not removed\"));\n\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertTrue(!helper.getEntities(EntityType.ITEM).get(0).getItem().has(RegistryEntries.DATACOMPONENT_PART_STATE), Component.literal(\"Dropped part must not have a state\"));\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableRemovePickaxe(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.north().north(), Direction.SOUTH);\n\n        // Remove redstone reader as player with pickaxe\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter().add(0.25, -1.5, -0.5));\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Part was not removed\"));\n\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertTrue(!helper.getEntities(EntityType.ITEM).get(0).getItem().has(RegistryEntries.DATACOMPONENT_PART_STATE), Component.literal(\"Dropped part must not have a state\"));\n            helper.assertItemEntityNotPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableRemoveTnt(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.north().north(), Direction.SOUTH);\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Placed part is not missing\"));\n\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableRemoveCableWrench(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Remove redstone reader as player with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.setShiftKeyDown(true); // To remove part!\n        player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(RegistryEntries.ITEM_WRENCH.value()));\n        player.setPos(helper.absolutePos(POS).getCenter().add(0, -1.5, 0.5));\n        player.setYRot(180);\n        helper.getBlockState(POS).useItemOn(player.getItemInHand(InteractionHand.MAIN_HAND), helper.getLevel(), player,\n                InteractionHand.MAIN_HAND,\n                new BlockHitResult(\n                        helper.absolutePos(POS).getCenter(),\n                        Direction.SOUTH,\n                        helper.absolutePos(POS),\n                        false)\n        );\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(!CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS), null), Component.literal(\"Cable is not fake\"));\n\n            helper.assertItemEntityNotPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertItemEntityPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableRemoveCableWithPickaxe(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Remove redstone reader as player with pickaxe\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack toolStack = new ItemStack(Items.DIAMOND_PICKAXE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, toolStack);\n        player.setPos(helper.absolutePos(POS).getCenter().add(0, -1.5, 0.5));\n        player.setYRot(180);\n        helper.getBlockState(POS).onDestroyedByPlayer(helper.getLevel(), helper.absolutePos(POS), player, toolStack, true, helper.getLevel().getFluidState(helper.absolutePos(POS)));\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(!CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS), null), Component.literal(\"Cable is not fake\"));\n\n            helper.assertItemEntityNotPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertItemEntityPresent(RegistryEntries.ITEM_CABLE.get());\n            helper.assertBlockPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderOnCableRemoveTnt(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Destroy with TNT\n        helper.setBlock(POS.above(), Blocks.TNT);\n        helper.setBlock(POS.above().above(), Blocks.REDSTONE_BLOCK);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder == null, Component.literal(\"Placed part is not missing\"));\n\n            helper.assertItemEntityPresent(PartTypes.REDSTONE_READER.getItem());\n            helper.assertTrue(!helper.getEntities(EntityType.ITEM).get(0).getItem().has(RegistryEntries.DATACOMPONENT_PART_STATE), Component.literal(\"Dropped part must not have a state\"));\n            helper.assertBlockNotPresent(RegistryEntries.BLOCK_CABLE.value(), POS);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithStateOnCable(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartStateReaderBase<PartTypeRedstoneReader> state = PartTypes.REDSTONE_READER.constructDefaultState();\n        state.generateId();\n        ItemStack partStack = PartTypes.REDSTONE_READER.getItemStack(ValueDeseralizationContext.of(helper.getLevel()), new PartPathElement(POS), state, true);\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, partStack);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertValueEqual(partStateHolder.getState().getId(), state.getId(), Component.literal(\"Part id is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithStateOnCableAsPlayer(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        PartStateReaderBase<PartTypeRedstoneReader> state = PartTypes.REDSTONE_READER.constructDefaultState();\n        state.generateId();\n        ItemStack partStack = PartTypes.REDSTONE_READER.getItemStack(ValueDeseralizationContext.of(helper.getLevel()), new PartPathElement(POS), state, true);\n        player.setItemInHand(InteractionHand.MAIN_HAND, partStack);\n        helper.placeAt(player, partStack, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertValueEqual(partStateHolder.getState().getId(), state.getId(), Component.literal(\"Part id is incorrect\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsBlocksCableConnection(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place cable facing redstone reader\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_CABLE.value());\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.north()), null);\n            helper.assertTrue(network1 != network2, Component.literal(\"Networks of cables with part inbetween are equal\"));\n\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS)),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.north())),\n                    Sets.newHashSet(),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithLever(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone reader\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.NORTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Attempt to place lever as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(Items.LEVER);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(player.getItemInHand(InteractionHand.MAIN_HAND).isEmpty(), Component.literal(\"Item hand is not empty\"));\n            helper.assertBlockPresent(Blocks.LEVER, POS.north());\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableAsPlayerAddCable(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        helper.placeAt(player, itemStack, POS.south(), Direction.NORTH);\n\n        // Place cable afterwards as player\n        ItemStack itemStackCable = new ItemStack(RegistryEntries.ITEM_CABLE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackCable);\n        helper.placeAt(player, itemStackCable, POS.south(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            PartHelpers.PartStateHolder<?, ?> partStateHolder = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.SOUTH));\n            helper.assertTrue(partStateHolder != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS.north()), null), Component.literal(\"Cable is fake\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testPartsRedstoneReaderWithoutCableAsPlayerAddCableTwo(GameTestHelper helper) {\n        // No cable!\n\n        // Place redstone reader as player\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(PartTypes.REDSTONE_READER.getItem());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack.copy());\n        helper.placeAt(player, itemStack.copy(), POS.south(), Direction.NORTH);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack.copy());\n        helper.placeAt(player, itemStack.copy(), POS.south().east(), Direction.NORTH);\n\n        // Place cable afterwards as player\n        ItemStack itemStackCable = new ItemStack(RegistryEntries.ITEM_CABLE);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackCable.copy());\n        helper.placeAt(player, itemStackCable.copy(), POS.south(), Direction.NORTH);\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStackCable.copy());\n        helper.placeAt(player, itemStackCable.copy(), POS.south().east(), Direction.NORTH);\n\n        helper.succeedWhen(() -> {\n            INetwork network1 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.north()), null);\n            INetwork network2 = NetworkHelpers.getNetworkChecked(helper.getLevel(), helper.absolutePos(POS.north().east()), null);\n            helper.assertTrue(network1 == network2, Component.literal(\"Networks of connected cables are not equal\"));\n\n            PartHelpers.PartStateHolder<?, ?> partStateHolder1 = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.north()), Direction.SOUTH));\n            helper.assertTrue(partStateHolder1 != null, Component.literal(\"Placed part is missing\"));\n            helper.assertTrue(partStateHolder1.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part is incorrect\"));\n            helper.assertTrue(partStateHolder1.getState().isEnabled(), Component.literal(\"Placed part is not enabled\"));\n            helper.assertTrue(partStateHolder1.getState().getChannel() == 0, Component.literal(\"Placed part is not on channel 0\"));\n            helper.assertTrue(CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS.north()), null), Component.literal(\"Cable is fake\"));\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.north())),\n                    Sets.newHashSet(Direction.EAST),\n                    Component.literal(\"Connected cables are invalid\")\n            );\n\n            PartHelpers.PartStateHolder<?, ?> partStateHolder2 = PartHelpers.getPart(PartPos.of(helper.getLevel(), helper.absolutePos(POS.north().east()), Direction.SOUTH));\n            helper.assertTrue(partStateHolder2 != null, Component.literal(\"Placed part 2 is missing\"));\n            helper.assertTrue(partStateHolder2.getPart() == PartTypes.REDSTONE_READER, Component.literal(\"Placed part 2 is incorrect\"));\n            helper.assertTrue(partStateHolder2.getState().isEnabled(), Component.literal(\"Placed part 2 is not enabled\"));\n            helper.assertTrue(partStateHolder2.getState().getChannel() == 0, Component.literal(\"Placed part 2 is not on channel 0\"));\n            helper.assertTrue(CableHelpers.isNoFakeCable(helper.getLevel(), helper.absolutePos(POS.north().east()), null), Component.literal(\"Cable 2 is fake\"));\n            helper.assertValueEqual(\n                    CableHelpers.getExternallyConnectedCables(helper.getLevel(), helper.absolutePos(POS.north().east())),\n                    Sets.newHashSet(Direction.WEST),\n                    Component.literal(\"Connected cables 2 are invalid\")\n            );\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsPerformance.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport com.google.common.math.Stats;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.gametest.framework.GameTestAssertException;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.TimeUtil;\nimport org.apache.logging.log4j.Level;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.command.CommandGenerateNetwork;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\n/**\n * Game tests for performance benchmarking of network operations.\n * These tests generate networks with different presets and measure their performance.\n * Results are written to run/logs/benchmark_results.txt for CI processing.\n * @author rubensworks\n */\npublic class GameTestsPerformance {\n\n    public static final int EXECUTION_SECONDS = 10;\n    public static final int WARMUP_TICKS = 200;\n    public static final int RADIUS = 10; // Max 10, as it would otherwise leak out of the template.\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos START_POS = BlockPos.ZERO.offset(1, 1, 1);\n\n    private static final String RESULTS_FILE = \"logs/benchmark_results.txt\";\n\n    /**\n     * Check if performance benchmarking is enabled via environment variable.\n     *\n     * @return true if PERFORMANCE_BENCHMARK_ENABLED environment variable is set to \"true\"\n     */\n    private static boolean isBenchmarkingEnabled() {\n        // Check environment variable first\n        String envVar = System.getenv(\"PERFORMANCE_BENCHMARK_ENABLED\");\n        if (envVar != null && \"true\".equalsIgnoreCase(envVar)) {\n            return true;\n        }\n\n        // Check system property as fallback\n        String sysProp = System.getProperty(\"PERFORMANCE_BENCHMARK_ENABLED\");\n        return sysProp != null && \"true\".equalsIgnoreCase(sysProp);\n    }\n\n    static {\n        if (isBenchmarkingEnabled()) {\n            // Initialize empty file\n            writeResults(new ArrayList<>(), false);\n        }\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_empty\")\n    public void testPerformanceEmptyNetwork(GameTestHelper helper) {\n        testPerformance(helper, \"empty\", (measureServerTickTimeNow) -> CommandGenerateNetwork.NetworkGenerationHelper.generateEmptyNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_idle\")\n    public void testPerformanceIdleNetwork(GameTestHelper helper) {\n        testPerformance(helper, \"idle\", (measureServerTickTimeNow) -> CommandGenerateNetwork.NetworkGenerationHelper.generateIdleNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_redstoneioclock\")\n    public void testPerformanceRedstoneNetwork(GameTestHelper helper) {\n        testPerformance(helper, \"redstoneioclock\", (measureServerTickTimeNow) -> CommandGenerateNetwork.NetworkGenerationHelper.generateRedstoneNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_redstoneioclockvariables\")\n    public void testPerformanceRedstoneNetworkVariables(GameTestHelper helper) {\n        testPerformance(helper, \"redstoneioclock_choice\", (measureServerTickTimeNow) -> CommandGenerateNetwork.NetworkGenerationHelper.generateRedstoneNetworkVariables(helper.getLevel(), helper.absolutePos(START_POS), RADIUS));\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_empty_append\")\n    public void testPerformanceEmptyNetworkAppend(GameTestHelper helper) {\n        testPerformance(helper, \"empty_append\", (measureServerTickTimeNow) -> {\n            CommandGenerateNetwork.NetworkGenerationHelper.generateEmptyNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n            addCablesPostWarmup(helper, 100, WARMUP_TICKS);\n            helper.runAfterDelay(WARMUP_TICKS + 100, measureServerTickTimeNow); // Measure server tick time right after cables have been added\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_redstoneioclock_append\")\n    public void testPerformanceIdleNetworkAppend(GameTestHelper helper) {\n        testPerformance(helper, \"redstoneioclock_append\", (measureServerTickTimeNow) -> {\n            CommandGenerateNetwork.NetworkGenerationHelper.generateRedstoneNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n            addCablesPostWarmup(helper, 100, WARMUP_TICKS);\n            helper.runAfterDelay(WARMUP_TICKS + 100, measureServerTickTimeNow); // Measure server tick time right after cables have been added\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_empty_remove\")\n    public void testPerformanceEmptyNetworkRemove(GameTestHelper helper) {\n        testPerformance(helper, \"empty_remove\", (measureServerTickTimeNow) -> {\n            CommandGenerateNetwork.NetworkGenerationHelper.generateEmptyNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n            removeCablesPostWarmup(helper, RADIUS, 100, WARMUP_TICKS);\n            helper.runAfterDelay(WARMUP_TICKS + 100, measureServerTickTimeNow); // Measure server tick time right after cables have been removed\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_redstoneioclock_remove\")\n    public void testPerformanceRedstoneNetworkRemove(GameTestHelper helper) {\n        testPerformance(helper, \"redstoneioclock_remove\", (measureServerTickTimeNow) -> {\n            CommandGenerateNetwork.NetworkGenerationHelper.generateRedstoneNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n            removeCablesPostWarmup(helper, RADIUS, 100, WARMUP_TICKS);\n            helper.runAfterDelay(WARMUP_TICKS + 100, measureServerTickTimeNow); // Measure server tick time right after cables have been removed\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_empty_appendparts\")\n    public void testPerformanceEmptyNetworkAppendParts(GameTestHelper helper) {\n        testPerformance(helper, \"empty_appendparts\", (measureServerTickTimeNow) -> {\n            CommandGenerateNetwork.NetworkGenerationHelper.generateEmptyNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n            addPartsPostWarmup(helper, RADIUS, 100, WARMUP_TICKS);\n            helper.runAfterDelay(WARMUP_TICKS + 100, measureServerTickTimeNow); // Measure server tick time right after parts have been added\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = (EXECUTION_SECONDS + 10) * 20, environment = Reference.MOD_ID + \":performance_redstoneioclock_appendparts\")\n    public void testPerformanceRedstoneNetworkAppendParts(GameTestHelper helper) {\n        testPerformance(helper, \"redstoneioclock_appendparts\", (measureServerTickTimeNow) -> {\n            CommandGenerateNetwork.NetworkGenerationHelper.generateRedstoneNetwork(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n            addPartsPostWarmup(helper, RADIUS, 100, WARMUP_TICKS);\n            helper.runAfterDelay(WARMUP_TICKS + 100, measureServerTickTimeNow); // Measure server tick time right after parts have been added\n        });\n    }\n\n    public static void testPerformance(GameTestHelper helper, String networkName, Consumer<Runnable> networkConstructor) {\n        if (!isBenchmarkingEnabled()) {\n            IntegratedDynamics.clog(Level.INFO, \"Performance benchmarking disabled (PERFORMANCE_BENCHMARK_ENABLED not set)\");\n            helper.succeed();\n            return;\n        }\n\n        ensureResultsDirectory();\n\n        // Calculate average server-wide tick time\n        Wrapper<Double> avgServerTickTime = new Wrapper<>(0D);\n        Runnable measureServerTickTimeNow = () -> avgServerTickTime.set(Stats.meanOf(helper.getLevel().getServer().getTickTimesNanos()) / TimeUtil.NANOSECONDS_PER_MILLISECOND);\n        networkConstructor.accept(measureServerTickTimeNow);\n\n        // Measure the network performance\n        String measurementId = networkName + \"_\" + System.currentTimeMillis();\n        Wrapper<UUID> measurementUUID = new Wrapper<>();\n        helper.runAfterDelay(WARMUP_TICKS, () -> {\n            // Wait a few seconds to warm up the code before starting measurement\n            measurementUUID.set(NetworkDiagnostics.getInstance().startMeasurementWithoutPlayer(measurementId, EXECUTION_SECONDS));\n        });\n\n        // Wait for measurement to complete, then retrieve results\n        helper.succeedWhen(() -> {\n            if (measurementUUID.get() == null || !NetworkDiagnostics.getInstance().isMeasurementComplete(measurementUUID.get())) {\n                throw new GameTestAssertException(Component.literal(\"Measurement did not complete in time: \" + measurementId), (int) helper.getTick());\n            }\n\n            double avgTickTime = NetworkDiagnostics.getInstance().getMeasurementAverageTickTime(measurementUUID.get());\n            NetworkDiagnostics.getInstance().clearMeasurement(measurementUUID.get());\n\n            // Calculate average server-wide tick time\n            if (avgServerTickTime.get() == 0D) {\n                measureServerTickTimeNow.run();\n            }\n\n            List<String> results = new ArrayList<>();\n            results.add(String.format(\"preset=%s size=%d avgNetworkTickTime=%.2f avgServerTickTime=%.2f\", networkName, RADIUS, avgTickTime, avgServerTickTime.get()));\n            writeResults(results, true);\n\n            CommandGenerateNetwork.NetworkGenerationHelper.clearCables(helper.getLevel(), helper.absolutePos(START_POS), RADIUS);\n        });\n    }\n\n    private static void ensureResultsDirectory() {\n        try {\n            Files.createDirectories(Paths.get(\"logs\"));\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static synchronized void writeResults(List<String> results, boolean append) {\n        try {\n            String content = String.join(\"\\n\", results);\n            if (append && Files.exists(Paths.get(RESULTS_FILE))) {\n                String existingString = Files.readString(Paths.get(RESULTS_FILE));\n                content = (existingString.isEmpty() ? content : existingString + content) + \"\\n\";\n            }\n            Files.write(Paths.get(RESULTS_FILE), content.getBytes());\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static void addCablesPostWarmup(GameTestHelper helper, int count, int delayOffset) {\n        for (int i = 0; i < count; i++) {\n            final int index = i;\n            helper.runAfterDelay(delayOffset + i, () -> {\n                // Add a cable at the correct position\n                BlockPos pos = helper.absolutePos(START_POS).offset(0, index, - 1);\n                CommandGenerateNetwork.NetworkGenerationHelper.placeCable(helper.getLevel(), pos);\n            });\n        }\n    }\n\n    private static void removeCablesPostWarmup(GameTestHelper helper, int radius, int count, int delayOffset) {\n        for (int i = 0; i < count; i++) {\n            final int index = i;\n            helper.runAfterDelay(delayOffset + i, () -> helper.destroyBlock(START_POS.offset(index / radius, index % radius, 0)));\n        }\n    }\n\n    private static void addPartsPostWarmup(GameTestHelper helper, int radius, int count, int delayOffset) {\n        for (int i = 0; i < count; i++) {\n            final int index = i;\n            helper.runAfterDelay(delayOffset + i, () -> {\n                // Add a part to NORTH-facing cable at the correct position\n                BlockPos pos = helper.absolutePos(START_POS).offset(index / radius, index % radius, 0);\n                CommandGenerateNetwork.NetworkGenerationHelper.addPartToNorthFace(helper.getLevel(), pos);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsProxy.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypePanelDisplay;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport static org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics.*;\n\npublic class GameTestsProxy {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testProxyFromAddition(GameTestHelper helper) {\n        // Place cable\n        helper.setBlock(POS, RegistryEntries.BLOCK_CABLE.value());\n        helper.setBlock(POS.east(), RegistryEntries.BLOCK_CABLE.value());\n\n        // Place redstone readers\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.WEST, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH, PartTypes.REDSTONE_READER, new ItemStack(PartTypes.REDSTONE_READER.getItem()));\n\n        // Place proxy\n        helper.setBlock(POS.below(), RegistryEntries.BLOCK_PROXY.value());\n        BlockEntityProxy proxy = helper.getBlockEntity(POS.below(), BlockEntityProxy.class);\n\n        // Place display panel\n        PartHelpers.addPart(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST, PartTypes.DISPLAY_PANEL, new ItemStack(PartTypes.DISPLAY_PANEL.getItem()));\n\n        // Place variable store\n        helper.setBlock(POS.north(), RegistryEntries.BLOCK_VARIABLE_STORE.get());\n        BlockEntityVariablestore variableStore = helper.getBlockEntity(POS.north(), BlockEntityVariablestore.class);\n\n        // Produce a redstone signals\n        helper.setBlock(POS.west().below(), Blocks.STONE);\n        helper.setBlock(POS.west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west(), Blocks.REDSTONE_WIRE);\n        helper.setBlock(POS.west().west().west().below(), Blocks.STONE);\n        helper.setBlock(POS.west().west().west(), Blocks.REDSTONE_TORCH);\n        helper.setBlock(POS.south(), Blocks.REDSTONE_BLOCK);\n\n        // Writer redstone signal from redstone readers to variable card\n        ItemStack variableAspect1 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.WEST), Aspects.Read.Redstone.INTEGER_VALUE);\n        ItemStack variableAspect2 = createVariableFromReader(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS), Direction.SOUTH), Aspects.Read.Redstone.INTEGER_VALUE);\n\n        // Insert redstone signal variable in variable store\n        variableStore.getInventory().setItem(0, variableAspect1);\n        variableStore.getInventory().setItem(1, variableAspect2);\n\n        // Create variable card for + operator on variable aspect\n        ItemStack variableAdded = createVariableForOperator(helper.getLevel(), Operators.ARITHMETIC_ADDITION, new int[]{\n                getVariableFacade(helper.getLevel(), variableAspect1).getId(),\n                getVariableFacade(helper.getLevel(), variableAspect2).getId()\n        });\n\n        // Place variable in proxy\n        proxy.getInventory().setItem(0, variableAdded);\n        proxy.getInventory().setItem(1, new ItemStack(RegistryEntries.ITEM_VARIABLE.get()));\n\n        // Wait a tick for the proxy to write\n        Wrapper<Pair<PartTypePanelDisplay, PartTypePanelDisplay.State>> partAndState = new Wrapper<>(null);\n        helper.runAfterDelay(1, () -> {\n            ItemStack variableProxied = proxy.getInventory().getItem(2);\n\n            // Place proxied variable in writer\n            partAndState.set(placeVariableInDisplayPanel(helper.getLevel(), PartPos.of(helper.getLevel(), helper.absolutePos(POS.east()), Direction.EAST), variableProxied));\n        });\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(partAndState.get() != null, Component.literal(\"No variable was proxied\"));\n            assertValueEqual(helper, partAndState.get().getRight().getDisplayValue(), ValueTypeInteger.ValueInteger.of(29));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.animal.goat.Goat;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.datastructure.Wrapper;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntitySqueezer;\n\npublic class GameTestsSqueezer {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(2, 1, 2);\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testSqueezer(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_SQUEEZER.value());\n        BlockEntitySqueezer machine = helper.getBlockEntity(POS, BlockEntitySqueezer.class);\n\n        // Set input\n        machine.getInventory().setItem(0, new ItemStack(RegistryEntries.BLOCK_MENRIL_LOG.get()));\n\n        // Let mob fall on the squeezer\n        Wrapper<Goat> entity = new Wrapper<>(helper.spawnWithNoFreeWill(EntityType.GOAT, POS.offset(0, 8, 0)));\n        helper.runAfterDelay(20, () -> {\n            entity.get().die(helper.getLevel().damageSources().generic());\n            entity.set(helper.spawnWithNoFreeWill(EntityType.GOAT, POS.offset(0, 8, 0)));\n        });\n\n        helper.succeedWhen(() -> {\n            helper.assertTrue(machine.getInventory().getItem(0).isEmpty(), Component.literal(\"Machine did not consume input\"));\n            helper.assertValueEqual(machine.getTank().getFluid().getFluid(), RegistryEntries.FLUID_MENRIL_RESIN.get(), Component.literal(\"Machine did not produce fluid output\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testSqueezerPlaceSingleItem(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_SQUEEZER.value());\n        BlockEntitySqueezer machine = helper.getBlockEntity(POS, BlockEntitySqueezer.class);\n\n        // Player right-clicks empty squeezer with a single item\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.getInventory().setItem(player.getInventory().getSelectedSlot(), new ItemStack(Items.DIRT, 1));\n\n        BlockState blockState = helper.getLevel().getBlockState(helper.absolutePos(POS));\n        blockState.useWithoutItem(helper.getLevel(), player, new BlockHitResult(helper.absolutePos(POS).getCenter(), Direction.UP, helper.absolutePos(POS), false));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), Items.DIRT, Component.literal(\"Squeezer did not receive item\"));\n            helper.assertTrue(player.getInventory().getSelectedItem().isEmpty(), Component.literal(\"Player hand should be empty after placing single item\"));\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testSqueezerPlaceStackItem(GameTestHelper helper) {\n        // Place machine\n        helper.setBlock(POS, RegistryEntries.BLOCK_SQUEEZER.value());\n        BlockEntitySqueezer machine = helper.getBlockEntity(POS, BlockEntitySqueezer.class);\n\n        // Player right-clicks empty squeezer with a stack of 5 items\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        player.getInventory().setItem(player.getInventory().getSelectedSlot(), new ItemStack(Items.DIRT, 5));\n\n        BlockState blockState = helper.getLevel().getBlockState(helper.absolutePos(POS));\n        blockState.useWithoutItem(helper.getLevel(), player, new BlockHitResult(helper.absolutePos(POS).getCenter(), Direction.UP, helper.absolutePos(POS), false));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(machine.getInventory().getItem(0).getItem(), Items.DIRT, Component.literal(\"Squeezer did not receive item from stack\"));\n            helper.assertValueEqual(player.getInventory().getSelectedItem().getCount(), 4, Component.literal(\"Player stack should be decremented by 1 after placing item\"));\n        });\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/GameTestsWrench.java",
    "content": "package org.cyclops.integrateddynamics.gametest;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.network.chat.Component;\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.UseOnContext;\nimport net.minecraft.world.level.GameType;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.cyclopscore.gametest.GameTest;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\npublic class GameTestsWrench {\n\n    public static final String TEMPLATE_EMPTY = \"integrateddynamics:empty10\";\n    public static final BlockPos POS = BlockPos.ZERO.offset(1, 1, 1);\n\n    protected void instantiateBarrelTest(GameTestHelper helper, Direction barrelDirectionStart, Direction facingDirection, Direction barrelDirectionExpected) {\n        // Place log\n        helper.setBlock(POS, Blocks.BARREL.defaultBlockState()\n                .setValue(BlockStateProperties.FACING, barrelDirectionStart));\n\n        // Right click with wrench\n        Player player = helper.makeMockPlayer(GameType.SURVIVAL);\n        ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_WRENCH.value());\n        player.setItemInHand(InteractionHand.MAIN_HAND, itemStack);\n        InteractionResult result = itemStack.useOn(new UseOnContext(player, player.getUsedItemHand(), new BlockHitResult(POS.getCenter(), facingDirection, helper.absolutePos(POS), false)));\n\n        helper.succeedWhen(() -> {\n            helper.assertValueEqual(result, InteractionResult.SUCCESS, Component.literal(\"Interaction failed\"));\n            helper.assertBlockProperty(POS, BlockStateProperties.FACING, barrelDirectionExpected);\n        });\n    }\n\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelUp1(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.UP, Direction.UP, Direction.DOWN);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelUp2(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.UP, Direction.DOWN, Direction.DOWN);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelUp3(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.UP, Direction.NORTH, Direction.NORTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelUp4(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.UP, Direction.EAST, Direction.EAST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelUp5(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.UP, Direction.SOUTH, Direction.SOUTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelUp6(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.UP, Direction.WEST, Direction.WEST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelDown1(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.DOWN, Direction.UP, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelDown2(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.DOWN, Direction.DOWN, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelDown3(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.DOWN, Direction.NORTH, Direction.NORTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelDown4(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.DOWN, Direction.EAST, Direction.EAST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelDown5(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.DOWN, Direction.SOUTH, Direction.SOUTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelDown6(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.DOWN, Direction.WEST, Direction.WEST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelNorth1(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.NORTH, Direction.UP, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelNorth2(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.NORTH, Direction.DOWN, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelNorth3(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.NORTH, Direction.NORTH, Direction.EAST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelNorth4(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.NORTH, Direction.EAST, Direction.EAST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelNorth5(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.NORTH, Direction.SOUTH, Direction.EAST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelNorth6(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.NORTH, Direction.WEST, Direction.EAST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelEast1(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.EAST, Direction.UP, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelEast2(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.EAST, Direction.DOWN, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelEast3(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.EAST, Direction.NORTH, Direction.SOUTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelEast4(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.EAST, Direction.EAST, Direction.SOUTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelEast5(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.EAST, Direction.SOUTH, Direction.SOUTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelEast6(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.EAST, Direction.WEST, Direction.SOUTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelSouth1(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.SOUTH, Direction.UP, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelSouth2(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.SOUTH, Direction.DOWN, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelSouth3(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.SOUTH, Direction.NORTH, Direction.WEST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelSouth4(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.SOUTH, Direction.EAST, Direction.WEST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelSouth5(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.SOUTH, Direction.SOUTH, Direction.WEST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelSouth6(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.SOUTH, Direction.WEST, Direction.WEST);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelWest1(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.WEST, Direction.UP, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelWest2(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.WEST, Direction.DOWN, Direction.UP);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelWest3(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.WEST, Direction.NORTH, Direction.NORTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelWest4(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.WEST, Direction.EAST, Direction.NORTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelWest5(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.WEST, Direction.SOUTH, Direction.NORTH);\n    }\n    @GameTest(template = TEMPLATE_EMPTY)\n    public void testWrenchBarrelWest6(GameTestHelper helper) {\n        instantiateBarrelTest(helper, Direction.WEST, Direction.WEST, Direction.NORTH);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/fuzzing/FuzzingGameTestInstance.java",
    "content": "package org.cyclops.integrateddynamics.gametest.fuzzing;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.Holder;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.gametest.framework.GameTestInstance;\nimport net.minecraft.gametest.framework.TestData;\nimport net.minecraft.gametest.framework.TestEnvironmentDefinition;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.gametest.GameTestsFuzzing;\n\n/**\n * @author rubensworks\n */\npublic class FuzzingGameTestInstance extends GameTestInstance {\n\n    public static final MapCodec<? extends GameTestInstance> CODEC = RecordCodecBuilder.<FuzzingGameTestInstance>mapCodec(instance -> instance.group(\n            TestData.CODEC.forGetter(FuzzingGameTestInstance::info),\n            Codec.INT.fieldOf(\"iteration\").forGetter(FuzzingGameTestInstance::getIteration)\n    ).apply(instance, FuzzingGameTestInstance::new));\n\n    private final int iteration;\n\n    public FuzzingGameTestInstance(TestData<Holder<TestEnvironmentDefinition<?>>> info, int iteration) {\n        super(info);\n        this.iteration = iteration;\n    }\n\n    public int getIteration() {\n        return iteration;\n    }\n\n    @Override\n    public void run(GameTestHelper gameTestHelper) {\n        GameTestsFuzzing.runFuzzingIteration(gameTestHelper, this.iteration);\n    }\n\n    @Override\n    public MapCodec<? extends GameTestInstance> codec() {\n        return CODEC;\n    }\n\n    @Override\n    protected MutableComponent typeDescription() {\n        return Component.literal(\"Fuzzing test iteration \" + this.iteration);\n    }\n\n    public Identifier getId() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"fuzzing_\" + this.iteration);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/fuzzing/FuzzingGameTestInstanceConfig.java",
    "content": "package org.cyclops.integrateddynamics.gametest.fuzzing;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.GameTestInstanceTypeConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\n\n/**\n * @author rubensworks\n */\npublic class FuzzingGameTestInstanceConfig extends GameTestInstanceTypeConfigCommon {\n    public FuzzingGameTestInstanceConfig(IModBase mod) {\n        super(mod, \"fuzzing\", (eConfig) -> FuzzingGameTestInstance.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/fuzzing/NetworkFuzzer.java",
    "content": "package org.cyclops.integrateddynamics.gametest.fuzzing;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EntitySpawnReason;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.FurnaceBlockEntity;\nimport net.minecraft.world.level.material.Fluid;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeCategory;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDryingBasin;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.gametest.GameTestHelpersIntegratedDynamics;\n\nimport javax.annotation.Nullable;\nimport java.util.*;\n\n/**\n * Generates random fuzzed networks with diverse parts, aspects, and operators.\n * Orchestrates three phases: writer selection, operator chain building, and reader selection.\n *\n * @author rubensworks\n */\npublic class NetworkFuzzer {\n\n    private final GameTestHelper helper;\n    private final Random random;\n    private final int maxOperatorDepth;\n    private final List<BlockPos> cables;\n    private final List<BlockEntityVariablestore> varStores;\n    private final ServerLevel level;\n\n    // Track variable store and slot for current variable\n    private int currentVarStoreIndex = 0;\n    private int varStoreSlot = 0;\n\n    // Track operator inputs that need reader sources\n    private record OperatorInputNeeded(IOperator operator, int inputIndex, IValueType<?> expectedType) {}\n\n    // Track variable IDs assigned to each operator's inputs\n    private static class OperatorVariableAssignment {\n        final IOperator operator;\n        final int[] variableIds;\n\n        OperatorVariableAssignment(IOperator operator, int numInputs) {\n            this.operator = operator;\n            this.variableIds = new int[numInputs];\n        }\n    }\n\n    public NetworkFuzzer(GameTestHelper helper, Random random, int maxOperatorDepth, List<BlockPos> cables,\n                         BlockEntityVariablestore varStore, ServerLevel level, BlockPos startPos) {\n        this.helper = helper;\n        this.random = random;\n        this.maxOperatorDepth = maxOperatorDepth;\n        this.cables = cables;\n        this.varStores = new ArrayList<>();\n        this.varStores.add(varStore);\n        this.level = level;\n    }\n\n    /**\n     * Get the current variable store.\n     */\n    private BlockEntityVariablestore getCurrentVarStore() {\n        return varStores.get(currentVarStoreIndex);\n    }\n\n    /**\n     * Advance to the next variable store slot, creating a new store on top if needed.\n     *\n     * @throws NetworkFuzzerException if creating a new store fails\n     */\n    private void advanceVarStoreSlot() throws NetworkFuzzerException {\n        varStoreSlot++;\n\n        if (varStoreSlot >= BlockEntityVariablestore.INVENTORY_SIZE) {\n            // Current store is full, create a new one on top\n            BlockEntityVariablestore currentStore = getCurrentVarStore();\n            BlockPos currentStorePos = currentStore.getBlockPos();\n\n            // Place new variable store on top of the current one\n            BlockPos newStorePos = currentStorePos.above();\n            level.setBlock(newStorePos, RegistryEntries.BLOCK_VARIABLE_STORE.get().defaultBlockState(), 2);\n\n            // Get the new variable store block entity\n            if (!(level.getBlockEntity(newStorePos) instanceof BlockEntityVariablestore newStore)) {\n                throw new NetworkFuzzerException(\"Failed to get new variable store block entity at \" + newStorePos);\n            }\n\n            // Add it to our list and update indices\n            varStores.add(newStore);\n            currentVarStoreIndex++;\n            varStoreSlot = 0;\n        }\n    }\n\n    /**\n     * Generate a fuzzed network with three phases:\n     * 1. Select a random writer part and aspect\n     * 2. Build an operator chain that produces the required input type\n     * 3. Select random readers to provide inputs to the operator chain\n     *\n     * @throws NetworkFuzzerException if an error occurs during network generation\n     * @return If the generation succeeded (false if it failed due to lack of space or parts, true otherwise)\n     */\n    public boolean generate() throws NetworkFuzzerException {\n        // Phase 1: Select writer\n        Pair<IPartTypeWriter<?, ?>, IAspectWrite<?, ?>> writerAspect = selectRandomWriter();\n\n        IPartTypeWriter<?, ?> writerType = writerAspect.getLeft();\n        IAspectWrite<?, ?> writeAspect = writerAspect.getRight();\n        IValueType<?> writerInputType = writeAspect.getValueType();\n\n        // Place writer part\n        Pair<BlockPos, Direction> writerPos = selectRandomOuterFace();\n        if (writerPos == null) {\n            return false; // No space to place writer\n        }\n\n        PartHelpers.addPart(level, writerPos.getLeft(), writerPos.getRight(),\n                writerType, new ItemStack(writerType.getItem()));\n\n        // Set up context blocks for the writer\n        setupWriterContextBlocks(writerType, writerPos.getLeft(), writerPos.getRight());\n\n        // Phase 2: Build operator chain\n        List<IOperator> operatorChain = buildOperatorChain(writerInputType);\n\n        // Phase 3: Select readers for unmapped inputs\n        List<OperatorInputNeeded> inputsNeeded = determineInputsNeeded(operatorChain);\n        Map<IOperator, OperatorVariableAssignment> operatorAssignments = new HashMap<>();\n        for (IOperator op : operatorChain) {\n            operatorAssignments.put(op, new OperatorVariableAssignment(op, op.getRequiredInputLength()));\n        }\n        fulfillInputs(inputsNeeded, operatorChain, operatorAssignments, writerPos.getLeft(), writerPos.getRight(), writeAspect);\n\n        return true;\n    }\n\n    /**\n     * Select a random writer part type and a random aspect from that part.\n     */\n    private Pair<IPartTypeWriter<?, ?>, IAspectWrite<?, ?>> selectRandomWriter() throws NetworkFuzzerException {\n        List<IPartTypeWriter<?, ?>> writers = new ArrayList<>();\n\n        // Collect all registered writer parts by checking if they implement IPartTypeWriter\n        for (Object partType : PartTypes.REGISTRY.getPartTypes()) {\n            if (partType instanceof IPartTypeWriter<?, ?> writer && !writer.getWriteAspects().isEmpty()) {\n                writers.add(writer);\n            }\n        }\n\n        if (writers.isEmpty()) {\n            throw new NetworkFuzzerException(\"No writer parts are available\");\n        }\n\n        IPartTypeWriter<?, ?> writerType = writers.get(random.nextInt(writers.size()));\n        List<?> writeAspects = writerType.getWriteAspects();\n\n        IAspectWrite<?, ?> aspect = (IAspectWrite<?, ?>) writeAspects.get(random.nextInt(writeAspects.size()));\n        return Pair.of(writerType, aspect);\n    }\n\n    /**\n     * Select a random outer face on the cable grid.\n     */\n    @Nullable\n    private Pair<BlockPos, Direction> selectRandomOuterFace() {\n        Set<BlockPos> cableSet = new HashSet<>(cables);\n        List<Pair<BlockPos, Direction>> outerFaces = new ArrayList<>();\n\n        for (BlockPos cable : cables) {\n            for (Direction dir : Direction.values()) {\n                BlockPos adjacent = cable.relative(dir);\n                if (!cableSet.contains(adjacent) && PartHelpers.getPart(PartPos.of(DimPos.of(level, cable), dir)) == null) {\n                    outerFaces.add(Pair.of(cable, dir));\n                }\n            }\n        }\n\n        if (outerFaces.isEmpty()) {\n            return null;\n        }\n\n        return outerFaces.get(random.nextInt(outerFaces.size()));\n    }\n\n    /**\n     * Build an operator chain that produces the required value type.\n     * Returns a list of operators in order from input to output.\n     *\n     * @throws NetworkFuzzerException if building the chain fails\n     */\n    private List<IOperator> buildOperatorChain(IValueType<?> requiredType) throws NetworkFuzzerException {\n        List<IOperator> chain = new ArrayList<>();\n        IValueType<?> currentType = requiredType;\n        int depth = random.nextInt(maxOperatorDepth + 1);\n\n        for (int i = 0; i < depth; i++) {\n            // Find an operator that produces currentType\n            IOperator op = findRandomOperatorProducing(currentType);\n            if (op == null) {\n                break;\n            }\n            chain.addFirst(op); // Add to front since we're building backwards\n\n            // Get the first input type of this operator\n            IValueType<?>[] inputTypes = op.getInputTypes();\n            if (inputTypes.length > 0) {\n                currentType = inputTypes[0];\n            } else {\n                break;\n            }\n        }\n\n        return chain;\n    }\n\n    /**\n     * Find a random operator that produces the given value type.\n     */\n    @Nullable\n    private IOperator findRandomOperatorProducing(IValueType<?> valueType) throws NetworkFuzzerException {\n        List<IOperator> matching = new ArrayList<>(findOperatorsProducingType(valueType));\n\n        if (matching.isEmpty()) {\n            return null;\n        }\n\n        return matching.get(random.nextInt(matching.size()));\n    }\n\n    /**\n     * Find operators producing a specific value type.\n     * This method uses Operators.REGISTRY.getOperatorsWithOutputType() to find all operators\n     * that produce the given type.\n     *\n     * @throws NetworkFuzzerException if the registry query fails\n     */\n    private List<IOperator> findOperatorsProducingType(IValueType<?> valueType) throws NetworkFuzzerException {\n        // Narrow down category types\n        if (valueType.isCategory()) {\n            IValueTypeCategory<?> valueTypeCategory = (IValueTypeCategory<?>) valueType;\n            List<IValueType> valueTypes = ValueTypes.REGISTRY.getValueTypes().stream()\n                    .filter(valueTypeCategory::correspondsTo)\n                    .toList();\n            valueType = valueTypes.get(random.nextInt(valueTypes.size()));\n        }\n\n        List<IOperator> result = new ArrayList<>();\n\n        try {\n            // Use the registry's built-in method to get operators with the specified output type\n            IValueType<?> finalValueType = valueType;\n            result.addAll(Operators.REGISTRY.getOperatorsWithOutputType(valueType)\n                    // Only keep operators that require entity inputs at a 10% chance.\n                    // Otherwise, the TARGETENTITY operator is overwhelmingly common since it is the only one that produces an entity type.\n                    .stream().filter(operator -> random.nextInt(10) == 9 || finalValueType == ValueTypes.OBJECT_ENTITY || Arrays.stream(operator.getInputTypes()).noneMatch(vt -> vt == ValueTypes.OBJECT_ENTITY)).toList());\n        } catch (Exception e) {\n            // If the registry query fails, throw an exception\n            throw new NetworkFuzzerException(\"Failed to find operators for type \" + valueType, e);\n        }\n\n        return result;\n    }\n\n    /**\n     * Determine which operator inputs still need to be satisfied.\n     */\n    private List<OperatorInputNeeded> determineInputsNeeded(List<IOperator> chain) {\n        List<OperatorInputNeeded> result = new ArrayList<>();\n\n        for (IOperator op : chain) {\n            IValueType<?>[] inputTypes = op.getInputTypes();\n            for (int i = 0; i < inputTypes.length; i++) {\n                // For now, mark all inputs as needed\n                // In a full implementation, we'd track which are already provided by previous operators\n                result.add(new OperatorInputNeeded(op, i, inputTypes[i]));\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Generate a random constant value for the given value type.\n     * Supports all value types except CATEGORY types.\n     */\n    @Nullable\n    private IValue generateRandomConstantValue(IValueType<?> valueType) {\n        // Skip category types\n        if (valueType.isCategory()) {\n            return null;\n        }\n\n        // Raw value types\n        if (valueType == ValueTypes.BOOLEAN) {\n            return ValueTypeBoolean.ValueBoolean.of(random.nextBoolean());\n        } else if (valueType == ValueTypes.INTEGER) {\n            return ValueTypeInteger.ValueInteger.of(random.nextInt(100) - 50);\n        } else if (valueType == ValueTypes.DOUBLE) {\n            return ValueTypeDouble.ValueDouble.of(random.nextDouble() * 100 - 50);\n        } else if (valueType == ValueTypes.LONG) {\n            return ValueTypeLong.ValueLong.of(random.nextLong());\n        } else if (valueType == ValueTypes.STRING) {\n            String[] examples = {\"test\", \"hello\", \"world\", \"value\", \"data\", \"abc\", \"xyz\"};\n            return ValueTypeString.ValueString.of(examples[random.nextInt(examples.length)]);\n        } else if (valueType == ValueTypes.LIST) {\n            // Return empty list with CATEGORY_ANY type\n            return ValueTypeList.ValueList.ofList(ValueTypes.CATEGORY_ANY, new ArrayList<>());\n        } else if (valueType == ValueTypes.OPERATOR) {\n            // Return a random operator wrapped as a value\n            try {\n                List<IOperator> operators = new ArrayList<>(Operators.REGISTRY.getOperators());\n                if (!operators.isEmpty()) {\n                    IOperator op = operators.get(random.nextInt(operators.size()));\n                    return ValueTypeOperator.ValueOperator.of(op);\n                }\n            } catch (Exception e) {\n                // If we can't get operators, return null\n            }\n            return null;\n        } else if (valueType == ValueTypes.NBT) {\n            CompoundTag tag = new CompoundTag();\n            tag.putString(\"fuzzer\", \"generated\");\n            return ValueTypeNbt.ValueNbt.of(tag);\n        }\n        // Object types\n        else if (valueType == ValueTypes.OBJECT_BLOCK) {\n            // Return a random block\n            var blocks = net.minecraft.core.registries.BuiltInRegistries.BLOCK.stream()\n                    .filter(block -> net.minecraft.core.registries.BuiltInRegistries.BLOCK.getKey(block)\n                            .getNamespace().equals(\"minecraft\"))\n                    .toList();\n            if (!blocks.isEmpty()) {\n                return ValueObjectTypeBlock.ValueBlock.of(blocks.get(random.nextInt(blocks.size())).defaultBlockState());\n            }\n            return null;\n        } else if (valueType == ValueTypes.OBJECT_ITEMSTACK) {\n            return ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(getRandomItem(), random.nextInt(64) + 1));\n        } else if (valueType == ValueTypes.OBJECT_ENTITY) {\n            // Return null for entity (can't easily create without spawning)\n            return null;\n        } else if (valueType == ValueTypes.OBJECT_FLUIDSTACK) {\n            // Return a water fluid stack\n            net.neoforged.neoforge.fluids.FluidStack fluidStack = new net.neoforged.neoforge.fluids.FluidStack(\n                    net.minecraft.world.level.material.Fluids.WATER, 1000);\n            return ValueObjectTypeFluidStack.ValueFluidStack.of(fluidStack);\n        } else if (valueType == ValueTypes.OBJECT_INGREDIENTS) {\n            // Return null for ingredients (complex to create)\n            return null;\n        } else if (valueType == ValueTypes.OBJECT_RECIPE) {\n            // Return null for recipe (complex to create)\n            return null;\n        }\n\n        return null;\n    }\n\n    /**\n     * Fulfill the operator inputs by selecting random readers or constant values.\n     * Tracks which variable IDs are assigned to each operator's inputs.\n     * 50% chance to use a constant value instead of placing a reader.\n     *\n     * @throws NetworkFuzzerException if fulfilling inputs fails\n     */\n    private void fulfillInputs(List<OperatorInputNeeded> inputsNeeded, List<IOperator> operatorChain,\n                               Map<IOperator, OperatorVariableAssignment> operatorAssignments,\n                               BlockPos writerPos, Direction writerDir,\n                               IAspectWrite<?, ?> writeAspect) throws NetworkFuzzerException {\n        for (OperatorInputNeeded input : inputsNeeded) {\n            // 50% chance to use a constant value instead of a reader\n            if (random.nextBoolean()) {\n                IValue constantValue = generateRandomConstantValue(input.expectedType);\n                if (constantValue != null) {\n                    // Create a variable from the constant value\n                    ItemStack constantVar = GameTestHelpersIntegratedDynamics.createVariableForValue(level, input.expectedType, constantValue);\n\n                    // Store the variable and track its ID\n                    int variableId = constantVar.getCapability(Capabilities.VariableFacade.ITEM).getVariableFacade(ValueDeseralizationContext.of(level)).getId();\n                    getCurrentVarStore().getInventory().setItem(varStoreSlot, constantVar);\n                    advanceVarStoreSlot();\n\n                    // Record this variable ID for the operator's input\n                    OperatorVariableAssignment assignment = operatorAssignments.get(input.operator);\n                    if (assignment != null && input.inputIndex < assignment.variableIds.length) {\n                        assignment.variableIds[input.inputIndex] = variableId;\n                    }\n                    continue;\n                }\n            }\n\n            // Place a reader (original behavior)\n            Pair<IPartTypeReader<?, ?>, IAspectRead<?, ?>> readerAspect = selectRandomReaderWithType(input.expectedType);\n            if (readerAspect == null) {\n                continue;\n            }\n\n            // Place reader part\n            Pair<BlockPos, Direction> readerPos = selectRandomOuterFace();\n            if (readerPos == null) {\n                continue;\n            }\n\n            IPartTypeReader<?, ?> readerType = readerAspect.getLeft();\n            IAspectRead<?, ?> readAspect = readerAspect.getRight();\n\n            PartHelpers.addPart(level, readerPos.getLeft(), readerPos.getRight(),\n                    readerType, new ItemStack(readerType.getItem()));\n\n            // Set up context blocks for the reader\n            setupReaderContextBlocks(readerType, readerPos.getLeft(), readerPos.getRight());\n\n            // Create variable from reader and store it\n            PartPos readerPartPos = PartPos.of(level, readerPos.getLeft(), readerPos.getRight());\n            PartHelpers.PartStateHolder<?, ?> readerHolder = PartHelpers.getPart(readerPartPos);\n            if (readerHolder == null) {\n                continue;\n            }\n\n            ItemStack readerVar = GameTestHelpersIntegratedDynamics.createVariableFromReader(level,\n                    readAspect, readerHolder.getState());\n\n            // Store the variable and track its ID\n            int variableId = readerVar.getCapability(Capabilities.VariableFacade.ITEM).getVariableFacade(ValueDeseralizationContext.of(level)).getId();\n            getCurrentVarStore().getInventory().setItem(varStoreSlot, readerVar);\n            advanceVarStoreSlot();\n\n            // Record this variable ID for the operator's input\n            OperatorVariableAssignment assignment = operatorAssignments.get(input.operator);\n            if (assignment != null && input.inputIndex < assignment.variableIds.length) {\n                assignment.variableIds[input.inputIndex] = variableId;\n            }\n        }\n\n        // Finally, create the variable for the writer based on operator chain or reader\n        PartPos writerPartPos = PartPos.of(level, writerPos, writerDir);\n        PartHelpers.PartStateHolder<?, ?> writerHolder = PartHelpers.getPart(writerPartPos);\n        if (writerHolder != null) {\n            ItemStack writerVar;\n\n            if (!operatorChain.isEmpty()) {\n                // Create variable from the last operator in the chain\n                IOperator lastOperator = operatorChain.getLast();\n                OperatorVariableAssignment assignment = operatorAssignments.get(lastOperator);\n                writerVar = GameTestHelpersIntegratedDynamics.createVariableForOperator(level, lastOperator, assignment.variableIds);\n            } else {\n                // No operators, create variable directly from a random constant\n                IValue writerValue = generateRandomConstantValue(writeAspect.getValueType());\n                if (writerValue != null) {\n                    // Create a variable from the constant value\n                    writerVar = GameTestHelpersIntegratedDynamics.createVariableForValue(level, writeAspect.getValueType(), writerValue);\n\n                    // Store the variable\n                    getCurrentVarStore().getInventory().setItem(varStoreSlot, writerVar);\n                    advanceVarStoreSlot();\n                } else {\n                    return;\n                }\n            }\n\n            GameTestHelpersIntegratedDynamics.placeVariableInWriter(helper, level, writerPartPos,\n                    writeAspect, writerVar);\n        }\n    }\n\n    /**\n     * Select a random reader part type and aspect that produces the required value type.\n     */\n    @Nullable\n    private Pair<IPartTypeReader<?, ?>, IAspectRead<?, ?>> selectRandomReaderWithType(IValueType<?> valueType) {\n        List<Pair<IPartTypeReader<?, ?>, IAspectRead<?, ?>>> validCombos = new ArrayList<>();\n\n        // Collect all registered reader parts by checking if they implement IPartTypeReader\n        for (Object partType : PartTypes.REGISTRY.getPartTypes()) {\n            if (partType instanceof IPartTypeReader<?, ?> reader) {\n                List<?> aspects = reader.getReadAspects();\n                for (Object aspect : aspects) {\n                    if (aspect instanceof IAspectRead<?, ?> readAspect) {\n                        if (readAspect.getValueType() == valueType) {\n                            validCombos.add(Pair.of(reader, readAspect));\n                        }\n                    }\n                }\n            }\n        }\n\n        if (validCombos.isEmpty()) {\n            return null;\n        }\n\n        return validCombos.get(random.nextInt(validCombos.size()));\n    }\n\n    /**\n     * Set up context blocks for different reader types.\n     * This method intelligently places blocks and entities based on reader part type names.\n     *\n     * @throws NetworkFuzzerException if setting up context blocks fails\n     */\n    private void setupReaderContextBlocks(IPartTypeReader<?, ?> readerType, BlockPos cablePos, Direction readerDir) throws NetworkFuzzerException {\n        BlockPos contextPos = cablePos.relative(readerDir);\n\n        if (!level.isEmptyBlock(contextPos)) {\n            return; // Don't overwrite existing blocks\n        }\n\n        String readerName = readerType.getClass().getSimpleName().toLowerCase();\n\n        // Place context blocks based on reader type\n        if (readerName.contains(\"inventory\")) {\n            placeChestWithItems(contextPos);\n        } else if (readerName.contains(\"fluid\")) {\n            placeFluidSource(contextPos);\n        } else if (readerName.contains(\"block\")) {\n            placeRandomBlock(contextPos);\n        } else if (readerName.contains(\"audio\")) {\n            level.setBlock(contextPos, Blocks.NOTE_BLOCK.defaultBlockState(), 2);\n        } else if (readerName.contains(\"redstone\")) {\n            placeRedstoneSource(contextPos);\n        } else if (readerName.contains(\"machine\")) {\n            placeFurnaceWithItems(contextPos);\n        } else if (readerName.contains(\"entity\")) {\n            spawnRandomEntity(contextPos);\n        }\n        // Network, World, and Extradimensional readers don't need special context blocks\n    }\n\n    /**\n     * Set up context blocks for different writer types.\n     * This method intelligently places blocks based on writer part type names.\n     * Currently supports redstone writers.\n     *\n     * @throws NetworkFuzzerException if setting up context blocks fails\n     */\n    private void setupWriterContextBlocks(IPartTypeWriter<?, ?> writerType, BlockPos cablePos, Direction writerDir) throws NetworkFuzzerException {\n        BlockPos contextPos = cablePos.relative(writerDir);\n\n        if (!level.isEmptyBlock(contextPos)) {\n            return; // Don't overwrite existing blocks\n        }\n\n        String writerName = writerType.getClass().getSimpleName().toLowerCase();\n\n        // Place context blocks based on writer type\n        if (writerName.contains(\"redstone\")) {\n            placeRedstoneWriterTarget(contextPos);\n        }\n        // Other writer types don't need special context blocks for now\n    }\n\n    /**\n     * Place a target block for redstone writers (redstone dust or redstone lamp).\n     */\n    private void placeRedstoneWriterTarget(BlockPos pos) {\n        // Randomly choose between redstone dust and redstone lamp\n        if (random.nextBoolean()) {\n            // Place redstone dust (needs a solid block beneath it)\n            BlockPos basePos = pos.below();\n            if (level.isEmptyBlock(basePos)) {\n                level.setBlock(basePos, Blocks.STONE.defaultBlockState(), 2);\n            }\n            level.setBlock(pos, Blocks.REDSTONE_WIRE.defaultBlockState(), 2);\n        } else {\n            // Place redstone lamp\n            level.setBlock(pos, Blocks.REDSTONE_LAMP.defaultBlockState(), 2);\n        }\n    }\n\n    /**\n     * Place a chest with random items inside.\n     */\n    private void placeChestWithItems(BlockPos pos) {\n        level.setBlock(pos, Blocks.CHEST.defaultBlockState(), 2);\n\n        // Add some random items to the chest\n        if (level.getBlockEntity(pos) instanceof net.minecraft.world.level.block.entity.ChestBlockEntity chestEntity) {\n            int itemCount = random.nextInt(3) + 1; // 1-3 random items\n            for (int i = 0; i < itemCount && i < chestEntity.getContainerSize(); i++) {\n                net.minecraft.world.item.ItemStack itemStack = new ItemStack(\n                        getRandomItem(),\n                        random.nextInt(64) + 1\n                );\n                chestEntity.setItem(i, itemStack);\n            }\n        }\n    }\n\n    /**\n     * Place a furnace block with random valid items in the input slot (0) and fuel slot (1).\n     * Each slot has a 1/10 chance of being left empty.\n     */\n    private void placeFurnaceWithItems(BlockPos pos) {\n        level.setBlock(pos, Blocks.FURNACE.defaultBlockState(), 2);\n\n        if (level.getBlockEntity(pos) instanceof FurnaceBlockEntity furnace) {\n            // Slot 0: input item (smeltable) — 1/10 chance to leave empty\n            if (random.nextInt(10) != 0) {\n                Item inputItem = getRandomSmeltableItem();\n                furnace.setItem(0, new ItemStack(inputItem, random.nextInt(64) + 1));\n            }\n\n            // Slot 1: fuel item — 1/10 chance to leave empty\n            if (random.nextInt(10) != 0) {\n                Item fuelItem = getRandomFuelItem();\n                furnace.setItem(1, new ItemStack(fuelItem, random.nextInt(64) + 1));\n            }\n        }\n    }\n\n    /**\n     * Get a random vanilla smeltable item (valid input for a furnace).\n     */\n    private net.minecraft.world.item.Item getRandomSmeltableItem() {\n        // A curated list of vanilla items that can be smelted\n        Item[] smeltables = {\n                Items.RAW_IRON,\n                Items.RAW_GOLD,\n                Items.RAW_COPPER,\n                Items.IRON_ORE,\n                Items.GOLD_ORE,\n                Items.COPPER_ORE,\n                Items.SAND,\n                Items.COBBLESTONE,\n                Items.NETHERRACK,\n                Items.CLAY_BALL,\n                Items.BEEF,\n                Items.PORKCHOP,\n                Items.POTATO,\n                Items.WET_SPONGE,\n                Items.ANCIENT_DEBRIS,\n        };\n        return smeltables[random.nextInt(smeltables.length)];\n    }\n\n    /**\n     * Get a random vanilla fuel item (valid fuel for a furnace).\n     * Uses getBurnTime to confirm validity, falling back to COAL if no fuel items are found.\n     */\n    private net.minecraft.world.item.Item getRandomFuelItem() {\n        // Filter all minecraft items to those that are valid furnace fuel\n        List<net.minecraft.world.item.Item> fuelItems = BuiltInRegistries.ITEM.stream()\n                .filter(item -> BuiltInRegistries.ITEM.getKey(item)\n                        .getNamespace().equals(\"minecraft\"))\n                .filter(item -> new ItemStack(item).getBurnTime(RecipeType.SMELTING, ServerLifecycleHooks.getCurrentServer().fuelValues()) > 0)\n                .toList();\n\n        if (fuelItems.isEmpty()) {\n            return net.minecraft.world.item.Items.COAL;\n        }\n        return fuelItems.get(random.nextInt(fuelItems.size()));\n    }\n\n    /**\n     * Get a random item for placing in containers.\n     * Only selects items from the minecraft namespace.\n     */\n    private net.minecraft.world.item.Item getRandomItem() {\n        // Filter to only minecraft namespace items and get a random one\n        var minecraftItems = BuiltInRegistries.ITEM.stream()\n                .filter(item -> BuiltInRegistries.ITEM.getKey(item)\n                        .getNamespace().equals(\"minecraft\"))\n                .toList();\n\n        if (minecraftItems.isEmpty()) {\n            return net.minecraft.world.item.Items.STONE;\n        }\n\n        return minecraftItems.get(random.nextInt(minecraftItems.size()));\n    }\n\n    /**\n     * Place a fluid source block (cauldron, fluid block, or drying basin with fluid).\n     * 50% chance to place a drying basin with a random fluid type and amount.\n     */\n    private void placeFluidSource(BlockPos pos) {\n        if (random.nextBoolean()) {\n            // 50% chance: place a drying basin with a random fluid and amount\n            placeDryingBasinWithFluid(pos);\n        } else {\n            // 50% chance: place cauldron or water block (original behavior)\n            if (random.nextBoolean()) {\n                level.setBlock(pos, Blocks.CAULDRON.defaultBlockState(), 2);\n            } else {\n                level.setBlock(pos, Blocks.WATER.defaultBlockState(), 2);\n            }\n        }\n    }\n\n    /**\n     * Place a drying basin block with a random fluid type and a random amount (1 to BUCKET_VOLUME).\n     */\n    private void placeDryingBasinWithFluid(BlockPos pos) {\n        level.setBlock(pos, RegistryEntries.BLOCK_DRYING_BASIN.get().defaultBlockState(), 2);\n\n        if (level.getBlockEntity(pos) instanceof BlockEntityDryingBasin dryingBasin) {\n            // Pick a random non-empty fluid from the minecraft namespace\n            List<Fluid> fluids = BuiltInRegistries.FLUID.stream()\n                    .filter(fluid -> !fluid.isSame(net.minecraft.world.level.material.Fluids.EMPTY)\n                            && BuiltInRegistries.FLUID.getKey(fluid)\n                                .getNamespace().equals(\"minecraft\"))\n                    .toList();\n\n            if (!fluids.isEmpty()) {\n                Fluid fluid = fluids.get(random.nextInt(fluids.size()));\n                int amount = random.nextInt(IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume()) + 1;\n                dryingBasin.getTank().setFluid(new FluidStack(fluid, amount));\n            }\n        }\n    }\n\n    /**\n     * Place a random solid block.\n     * Only selects blocks from the minecraft namespace.\n     */\n    private void placeRandomBlock(BlockPos pos) {\n        // Filter to only minecraft namespace blocks and get a random one\n        var minecraftBlocks = BuiltInRegistries.BLOCK.stream()\n                .filter(block -> BuiltInRegistries.BLOCK.getKey(block).getNamespace().equals(\"minecraft\"))\n                .filter(block -> !BuiltInRegistries.BLOCK.getKey(block).getPath().equals(\"command_block\"))\n                .toList();\n\n        if (!minecraftBlocks.isEmpty()) {\n            Block block = minecraftBlocks.get(random.nextInt(minecraftBlocks.size()));\n            level.setBlock(pos, block.defaultBlockState(), 2);\n        }\n    }\n\n    /**\n     * Place a redstone source block (torch, wire, repeater, etc).\n     */\n    private void placeRedstoneSource(BlockPos pos) {\n        // Randomly choose between different redstone sources\n        int choice = random.nextInt(4);\n        switch (choice) {\n            case 0:\n                // Redstone torch\n                level.setBlock(pos, Blocks.REDSTONE_TORCH.defaultBlockState(), 2);\n                break;\n            case 1:\n                // Redstone wire (place on top of a block)\n                BlockPos basePos = pos.below();\n                if (level.isEmptyBlock(basePos)) {\n                    level.setBlock(basePos, Blocks.STONE.defaultBlockState(), 2);\n                }\n                level.setBlock(pos, Blocks.REDSTONE_WIRE.defaultBlockState(), 2);\n                break;\n            case 2:\n                // Redstone repeater\n                level.setBlock(pos, Blocks.REPEATER.defaultBlockState(), 2);\n                break;\n            case 3:\n                // Redstone comparator\n                level.setBlock(pos, Blocks.COMPARATOR.defaultBlockState(), 2);\n                break;\n        }\n    }\n\n    /**\n     * Spawn a random entity in front of the reader.\n     * Only selects entity types from the minecraft namespace.\n     *\n     * @throws NetworkFuzzerException if spawning an entity fails\n     */\n    private void spawnRandomEntity(BlockPos pos) throws NetworkFuzzerException {\n        // Filter to only minecraft namespace entity types and get a random one\n        var minecraftEntities = BuiltInRegistries.ENTITY_TYPE.stream()\n                .filter(entityType -> BuiltInRegistries.ENTITY_TYPE.getKey(entityType)\n                        .getNamespace().equals(\"minecraft\"))\n                .toList();\n\n        if (minecraftEntities.isEmpty()) {\n            throw new NetworkFuzzerException(\"No entities are available to spawn\");\n        }\n\n        EntityType<?> entityType = minecraftEntities.get(random.nextInt(minecraftEntities.size()));\n\n        try {\n            Entity entity = entityType.create(level, EntitySpawnReason.COMMAND);\n            if (entity != null) {\n                entity.setPos(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);\n                level.addFreshEntity(entity);\n            }\n        } catch (Exception e) {\n            // Some entity types might fail to spawn\n            throw new NetworkFuzzerException(\"Failed to spawn entity of type \" + entityType, e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/fuzzing/NetworkFuzzerException.java",
    "content": "package org.cyclops.integrateddynamics.gametest.fuzzing;\n\n/**\n * Exception thrown by NetworkFuzzer when an error occurs during network generation.\n * This exception is intended to be caught and converted to a GameTestAssertException.\n *\n * @author rubensworks\n */\npublic class NetworkFuzzerException extends Exception {\n\n    public NetworkFuzzerException(String message) {\n        super(message);\n    }\n\n    public NetworkFuzzerException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/Asserts.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\n/**\n * @author rubensworks\n */\npublic class Asserts {\n\n    public static void check(final boolean expression, final String message) {\n        if (!expression) {\n            throw new IllegalStateException(message);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyValueType.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeClient;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueBase;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Consumer;\n\n/**\n * Dummy value type\n * @author rubensworks\n */\npublic class DummyValueType implements IValueType<DummyValueType.DummyValue> {\n\n    public static final DummyValueType TYPE = new DummyValueType();\n\n    @Override\n    public IValueTypeClient<DummyValue> getClient() {\n        return new IValueTypeClient<>() {};\n    }\n\n    @Override\n    public boolean isCategory() {\n        return false;\n    }\n\n    @Override\n    public boolean isObject() {\n        return false;\n    }\n\n    @Override\n    public DummyValue getDefault() {\n        return null;\n    }\n\n    @Override\n    public String getTypeName() {\n        return \"boolean\";\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return null;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return \"boolean\";\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo, @Nullable DummyValue value) {\n\n    }\n\n    @Override\n    public MutableComponent toCompactString(DummyValue value) {\n        return Component.literal(\"dummy\");\n    }\n\n    @Override\n    public int getDisplayColor() {\n        return 0;\n    }\n\n    @Override\n    public ChatFormatting getDisplayColorFormat() {\n        return ChatFormatting.WHITE;\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType valueType) {\n        return false;\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, DummyValue value) {\n    }\n\n    @Override\n    public Component canDeserialize(ValueInput valueInput) {\n        return null;\n    }\n\n    @Override\n    public DummyValue deserialize(ValueInput valueInput) {\n        return DummyValue.of();\n    }\n\n    @Override\n    public DummyValue materialize(DummyValue value) {\n        return value;\n    }\n\n    @Override\n    public String toString(DummyValue value) {\n        return \"DUMMY STRING\";\n    }\n\n    @Override\n    public DummyValue parseString(String value) throws EvaluationException {\n        return DummyValue.of();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return null;\n    }\n\n    @Override\n    public DummyValue cast(IValue value) throws EvaluationException {\n        return (DummyValue) value;\n    }\n\n    public static class DummyValue extends ValueBase {\n\n        private DummyValue() {\n            super(TYPE);\n        }\n\n        public static DummyValue of() {\n            return new DummyValue();\n        }\n\n        @Override\n        public String toString() {\n            return \"DummyValue\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariable.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariableInvalidateListener;\n\n/**\n * A dummy boolean variable.\n * @author rubensworks\n */\npublic class DummyVariable<V extends IValue> implements IVariable<V> {\n\n    private final IValueType<V> type;\n    private V value;\n    private boolean fetched = false;\n\n    public DummyVariable(IValueType<V> type, V value) {\n        this.type = type;\n        this.value = value;\n    }\n\n    public DummyVariable(IValueType<V> type) {\n        this.type = type;\n        this.value = type.getDefault();\n    }\n\n    @Override\n    public IValueType<V> getType() {\n        return type;\n    }\n\n    @Override\n    public V getValue() {\n        this.fetched = true;\n        return value;\n    }\n\n    @Override\n    public void invalidate() {\n\n    }\n\n    @Override\n    public void addInvalidationListener(IVariableInvalidateListener invalidateListener) {\n\n    }\n\n    @Override\n    public void removeInvalidationListener(IVariableInvalidateListener invalidateListener) {\n\n    }\n\n    public void setValue(V value) {\n        this.value = value;\n    }\n\n    public boolean isFetched() {\n        return this.fetched;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariableBlock.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeBlock;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Dummy block variable.\n * @author rubensworks\n */\npublic class DummyVariableBlock extends DummyVariable<ValueObjectTypeBlock.ValueBlock> {\n\n    public DummyVariableBlock(ValueObjectTypeBlock.ValueBlock value) {\n        super(ValueTypes.OBJECT_BLOCK, value);\n    }\n\n    public DummyVariableBlock() {\n        super(ValueTypes.OBJECT_BLOCK);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariableEntity.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeEntity;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Dummy entity variable.\n * @author rubensworks\n */\npublic class DummyVariableEntity extends DummyVariable<ValueObjectTypeEntity.ValueEntity> {\n\n    public DummyVariableEntity(ValueObjectTypeEntity.ValueEntity value) {\n        super(ValueTypes.OBJECT_ENTITY, value);\n    }\n\n    public DummyVariableEntity() {\n        super(ValueTypes.OBJECT_ENTITY);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariableFluidStack.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeFluidStack;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Dummy fluidstack variable.\n * @author rubensworks\n */\npublic class DummyVariableFluidStack extends DummyVariable<ValueObjectTypeFluidStack.ValueFluidStack> {\n\n    public DummyVariableFluidStack(ValueObjectTypeFluidStack.ValueFluidStack value) {\n        super(ValueTypes.OBJECT_FLUIDSTACK, value);\n    }\n\n    public DummyVariableFluidStack() {\n        super(ValueTypes.OBJECT_FLUIDSTACK);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariableIngredients.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Dummy ingredients variable.\n * @author rubensworks\n */\npublic class DummyVariableIngredients extends DummyVariable<ValueObjectTypeIngredients.ValueIngredients> {\n\n    public DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients value) {\n        super(ValueTypes.OBJECT_INGREDIENTS, value);\n    }\n\n    public DummyVariableIngredients() {\n        super(ValueTypes.OBJECT_INGREDIENTS);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariableItemStack.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Dummy itemstack variable.\n * @author rubensworks\n */\npublic class DummyVariableItemStack extends DummyVariable<ValueObjectTypeItemStack.ValueItemStack> {\n\n    public DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack value) {\n        super(ValueTypes.OBJECT_ITEMSTACK, value);\n    }\n\n    public DummyVariableItemStack() {\n        super(ValueTypes.OBJECT_ITEMSTACK);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/DummyVariableRecipe.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\n\n/**\n * Dummy recipe variable.\n * @author rubensworks\n */\npublic class DummyVariableRecipe extends DummyVariable<ValueObjectTypeRecipe.ValueRecipe> {\n\n    public DummyVariableRecipe(ValueObjectTypeRecipe.ValueRecipe value) {\n        super(ValueTypes.OBJECT_RECIPE, value);\n    }\n\n    public DummyVariableRecipe() {\n        super(ValueTypes.OBJECT_RECIPE);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/GameTester.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.Holder;\nimport net.minecraft.core.Registry;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.gametest.framework.GameTestInstance;\nimport net.minecraft.gametest.framework.TestData;\nimport net.minecraft.gametest.framework.TestEnvironmentDefinition;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.block.Rotation;\nimport org.cyclops.integrateddynamics.command.CommandTest;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.BiConsumer;\n\n/**\n * @author rubensworks\n */\npublic class GameTester {\n\n    public static GameTestHelper GAME_TEST_HELPER;\n\n    public static void registerCommonTests(String modId, BiConsumer<Identifier, GameTestInstance> registrar, Registry<TestEnvironmentDefinition<?>> testEnvironmentRegistry) {\n        for (IntegrationMethodGameTestInstance testInstance : GameTester.integrationTests(modId, testEnvironmentRegistry)) {\n            registrar.accept(testInstance.getId(), testInstance);\n        }\n    }\n\n    public static Collection<IntegrationMethodGameTestInstance> integrationTests(String modId, Registry<TestEnvironmentDefinition<?>> testEnvironmentRegistry) {\n        List<IntegrationMethodGameTestInstance> testsList = Lists.newArrayList();\n\n        for(String className : CommandTest.CLASSES) {\n            try {\n                Class<?> clazz = Class.forName(className);\n\n                // Collect test methods\n                List<Method> tests = com.google.common.collect.Lists.newLinkedList();\n                for(Method method : clazz.getDeclaredMethods()) {\n                    if(method.isAnnotationPresent(IntegrationTest.class)) {\n                        tests.add(method);\n                    }\n                }\n\n                // Run tests\n                for(Method method : tests) {\n                    Holder.Reference<TestEnvironmentDefinition<?>> environment = testEnvironmentRegistry.getOrThrow(ResourceKey.create(\n                            Registries.TEST_ENVIRONMENT,\n                            Identifier.parse(\"default\")\n                    ));\n                    testsList.add(new IntegrationMethodGameTestInstance(\n                            new TestData<Holder<TestEnvironmentDefinition<?>>>(\n                                    environment,\n                                    Identifier.parse(\"integrateddynamics:test\"),\n                                    1,\n                                    1,\n                                    true,\n                                    Rotation.NONE\n                            ),\n                            modId,\n                            clazz.getName(),\n                            method.getName()));\n                }\n            } catch (ClassNotFoundException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        return testsList;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/IntegrationMethodGameTestInstance.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.Holder;\nimport net.minecraft.gametest.framework.*;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Locale;\n\n/**\n * @author rubensworks\n */\npublic class IntegrationMethodGameTestInstance extends GameTestInstance {\n\n    public static final MapCodec<? extends GameTestInstance> CODEC = RecordCodecBuilder.<IntegrationMethodGameTestInstance>mapCodec(instance -> instance.group(\n            TestData.CODEC.forGetter(IntegrationMethodGameTestInstance::info),\n            Codec.STRING.fieldOf(\"modId\").forGetter(IntegrationMethodGameTestInstance::getModId),\n            Codec.STRING.fieldOf(\"class\").forGetter(IntegrationMethodGameTestInstance::getClassName),\n            Codec.STRING.fieldOf(\"method\").forGetter(IntegrationMethodGameTestInstance::getMethodName)\n    ).apply(instance, IntegrationMethodGameTestInstance::new));\n\n    private final String modId;\n    private final String className;\n    private final String methodName;\n\n    public IntegrationMethodGameTestInstance(TestData<Holder<TestEnvironmentDefinition<?>>> info, String modId, String className, String methodName) {\n        super(info);\n        this.modId = modId;\n        this.className = className;\n        this.methodName = methodName;\n    }\n\n    public String getModId() {\n        return modId;\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public String getMethodName() {\n        return methodName;\n    }\n\n    @Override\n    public void run(GameTestHelper gameTestHelper) {\n        try {\n            Class<?> clazz = Class.forName(this.className);\n            Object instance = clazz.newInstance();\n\n            // Call befores\n            List<Method> befores = com.google.common.collect.Lists.newLinkedList();\n            for(Method method : clazz.getDeclaredMethods()) {\n                if(method.isAnnotationPresent(IntegrationBefore.class)) {\n                    befores.add(method);\n                }\n            }\n\n            Method method = clazz.getMethod(this.methodName);\n\n            gameTestHelper.succeedIf(() -> {\n                GameTester.GAME_TEST_HELPER = gameTestHelper;\n                try {\n                    for(Method before : befores) {\n                        before.invoke(instance);\n                    }\n                    method.invoke(instance);\n                } catch (InvocationTargetException e) {\n                    Class<?> excepted = method.getAnnotation(IntegrationTest.class).expected();\n                    if(!excepted.isInstance(e.getTargetException())) {\n                        if (e.getTargetException() instanceof IllegalStateException || e.getTargetException() instanceof AssertionError) {\n                            e.getTargetException().printStackTrace();\n                            throw new GameTestAssertException(Component.literal(\"Test \" + getId().toString() + \" failed!\"), (int) gameTestHelper.getTick());\n                        } else {\n                            e.getTargetException().printStackTrace();\n                            throw new GameTestAssertException(Component.literal(String.format(\"Expected at %s exception %s, but found:\", getId().toString(), e)), (int) gameTestHelper.getTick());\n                        }\n                    }\n                } catch (IllegalAccessException e) {\n                    e.printStackTrace();\n                    throw new GameTestAssertException(Component.literal(e.getMessage()), (int) gameTestHelper.getTick());\n                }\n            });\n        } catch (IllegalAccessException | ClassNotFoundException | InstantiationException | NoSuchMethodException e) {\n            e.printStackTrace();\n            throw new GameTestAssertException(Component.literal(e.getMessage()), (int) gameTestHelper.getTick());\n        }\n    }\n\n    @Override\n    public MapCodec<? extends GameTestInstance> codec() {\n        return CODEC;\n    }\n\n    @Override\n    protected MutableComponent typeDescription() {\n        return Component.literal(\"Method-based test instance for \" + getClassName() + \".\" + getMethodName());\n    }\n\n    public Identifier getId() {\n        return Identifier.fromNamespaceAndPath(this.modId, (this.className.replaceAll(\"org.cyclops\\\\.[^.]*\\\\.[^.]*\\\\.\", \"\") + \".\" + this.methodName).toLowerCase(Locale.ROOT).replace('.', '_'));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/IntegrationMethodGameTestInstanceConfig.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.GameTestInstanceTypeConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\n\n/**\n * @author rubensworks\n */\npublic class IntegrationMethodGameTestInstanceConfig extends GameTestInstanceTypeConfigCommon {\n    public IntegrationMethodGameTestInstanceConfig(IModBase mod) {\n        super(mod, \"integration_method\", (eConfig) -> IntegrationMethodGameTestInstance.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestBlockOperators.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.nbt.StringTag;\nimport net.minecraft.sounds.SoundEvents;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.CropBlock;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\n/**\n * Test the different logical operators.\n * @author rubensworks\n */\npublic class TestBlockOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableBlock bAir;\n    private DummyVariableBlock bCoal;\n    private DummyVariableBlock bDarkOakLeaves;\n    private DummyVariableBlock bLogicProgrammer;\n    private DummyVariableBlock bLeaves;\n    private DummyVariableBlock bReed;\n    private DummyVariableBlock bSand;\n    private DummyVariableBlock bFarmLand;\n    private DummyVariableBlock bCarrot;\n    private DummyVariableBlock bCarrotGrown;\n\n    private DummyVariableItemStack iApple;\n    private DummyVariableItemStack iSeedWheat;\n\n    private DummyVariable<ValueTypeString.ValueString> sSponge;\n    private DummyVariable<ValueTypeString.ValueString> sSand;\n\n    private DummyVariable<ValueTypeNbt.ValueNbt> nbtCarrotGrown;\n\n    @IntegrationBefore\n    public void before() {\n        bAir = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.AIR.defaultBlockState()));\n        bCoal = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.COAL_BLOCK.defaultBlockState()));\n        bDarkOakLeaves = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.DARK_OAK_LEAVES.defaultBlockState()));\n        bLogicProgrammer = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(RegistryEntries.BLOCK_LOGIC_PROGRAMMER.get().defaultBlockState()));\n        bLeaves = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.OAK_LEAVES.defaultBlockState()));\n        bReed = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.SUGAR_CANE.defaultBlockState()));\n        bSand = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.SAND.defaultBlockState()));\n        bFarmLand = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.FARMLAND.defaultBlockState()));\n        bCarrot = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.CARROTS.defaultBlockState()));\n        bCarrotGrown = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.CARROTS.defaultBlockState().setValue(CropBlock.AGE, 1)));\n\n        iApple = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.APPLE)));\n        iSeedWheat = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.WHEAT_SEEDS)));\n\n        sSponge = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:sponge\"));\n        sSand = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:sand\"));\n\n        CompoundTag tag = new CompoundTag();\n        tag.putString(\"age\", \"1\");\n        nbtCarrotGrown = new DummyVariable<>(ValueTypes.NBT, ValueTypeNbt.ValueNbt.of(tag));\n    }\n\n    /**\n     * ----------------------------------- OPAQUE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockOpaque() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_OPAQUE.evaluate(new IVariable[]{bAir});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isopaque(air) = false\");\n\n        IValue res2 = Operators.OBJECT_BLOCK_OPAQUE.evaluate(new IVariable[]{bCoal});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isopaque(coalblock) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeOpaqueLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_OPAQUE.evaluate(new IVariable[]{bAir, bAir});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeOpaqueSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_OPAQUE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeOpaque() throws EvaluationException {\n        Operators.OBJECT_BLOCK_OPAQUE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ITEMSTACK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockItemStack() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_ITEMSTACK.evaluate(new IVariable[]{bAir});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an itemstack\");\n        TestHelpers.assertEqual(!((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().isEmpty(), false, \"itemstack(air) = null\");\n\n        IValue res2 = Operators.OBJECT_BLOCK_ITEMSTACK.evaluate(new IVariable[]{bCoal});\n        TestHelpers.assertEqual(ItemStack.isSameItem(((ValueObjectTypeItemStack.ValueItemStack) res2).getRawValue(), new ItemStack(Blocks.COAL_BLOCK)), true, \"itemstack(coalblock) = coalblock\");\n\n        IValue res3 = Operators.OBJECT_BLOCK_ITEMSTACK.evaluate(new IVariable[]{bDarkOakLeaves});\n        TestHelpers.assertEqual(ItemStack.isSameItem(((ValueObjectTypeItemStack.ValueItemStack) res3).getRawValue(), new ItemStack(Blocks.DARK_OAK_LEAVES, 1)), true, \"itemstack(dark_oak_leaves) = dark_oak_leaves\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemStackLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_ITEMSTACK.evaluate(new IVariable[]{bAir, bAir});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemStackSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_ITEMSTACK.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeItemStack() throws EvaluationException {\n        Operators.OBJECT_BLOCK_ITEMSTACK.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MODNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockModName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_MODNAME.evaluate(new IVariable[]{bAir});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"Minecraft\", \"modname(air) = Minecraft\");\n\n        IValue res2 = Operators.OBJECT_BLOCK_MODNAME.evaluate(new IVariable[]{bLogicProgrammer});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), \"IntegratedDynamics\", \"modname(logicprogrammer) = IntegratedDynamics\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_MODNAME.evaluate(new IVariable[]{bAir, bAir});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_MODNAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeModName() throws EvaluationException {\n        Operators.OBJECT_BLOCK_MODNAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- SOUNDS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockSound() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_BREAKSOUND.evaluate(new IVariable[]{bCoal});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), SoundEvents.STONE_BREAK.location().toString(), \"placesound(coal) = inecraft:block.stone.break\");\n\n        IValue res2 = Operators.OBJECT_BLOCK_PLACESOUND.evaluate(new IVariable[]{bCoal});\n        Asserts.check(res2 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), SoundEvents.STONE_PLACE.location().toString(), \"placesound(coal) = inecraft:block.stone.place\");\n\n        IValue res3 = Operators.OBJECT_BLOCK_STEPSOUND.evaluate(new IVariable[]{bCoal});\n        Asserts.check(res3 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res3).getRawValue(), SoundEvents.STONE_STEP.location().toString(), \"placesound(coal) = inecraft:block.stone.step\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_BREAKSOUND.evaluate(new IVariable[]{bAir, bAir});\n        Operators.OBJECT_BLOCK_PLACESOUND.evaluate(new IVariable[]{bAir, bAir});\n        Operators.OBJECT_BLOCK_STEPSOUND.evaluate(new IVariable[]{bAir, bAir});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_BREAKSOUND.evaluate(new IVariable[]{});\n        Operators.OBJECT_BLOCK_PLACESOUND.evaluate(new IVariable[]{});\n        Operators.OBJECT_BLOCK_STEPSOUND.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeSound() throws EvaluationException {\n        Operators.OBJECT_BLOCK_BREAKSOUND.evaluate(new IVariable[]{DUMMY_VARIABLE});\n        Operators.OBJECT_BLOCK_PLACESOUND.evaluate(new IVariable[]{DUMMY_VARIABLE});\n        Operators.OBJECT_BLOCK_STEPSOUND.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISSHEARABLE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsShearable() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_ISSHEARABLE.evaluate(new IVariable[]{bAir});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isshearable(air) = false\");\n\n        IValue res2 = Operators.OBJECT_BLOCK_ISSHEARABLE.evaluate(new IVariable[]{bLeaves});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isshearable(leaves) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsShearableLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_ISSHEARABLE.evaluate(new IVariable[]{bAir, bAir});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsShearableSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_ISSHEARABLE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsShearable() throws EvaluationException {\n        Operators.OBJECT_BLOCK_ISSHEARABLE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- PLANTAGE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockPlantAge() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_PLANTAGE.evaluate(new IVariable[]{bAir});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 0, \"plantage(air) = 0\");\n\n        IValue res2 = Operators.OBJECT_BLOCK_PLANTAGE.evaluate(new IVariable[]{bCarrot});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 0, \"plantage(bCarrot) = 0\");\n\n        IValue res3 = Operators.OBJECT_BLOCK_PLANTAGE.evaluate(new IVariable[]{bCarrotGrown});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res3).getRawValue(), 1, \"plantage(bCarrotGrown) = 1\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizePlantAgeLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_PLANTAGE.evaluate(new IVariable[]{bAir, bAir});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizePlantAgeSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_PLANTAGE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypePlantAge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_PLANTAGE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BLOCKBYNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockBlockByName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_BY_NAME.evaluate(new IVariable[]{sSponge});\n        Asserts.check(res1 instanceof ValueObjectTypeBlock.ValueBlock, \"result is a block\");\n        TestHelpers.assertEqual(((ValueObjectTypeBlock.ValueBlock) res1).getRawValue().get(), Blocks.SPONGE.defaultBlockState(), \"blockbyname(minecraft:sponge) = sponge\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockByNameLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_BY_NAME.evaluate(new IVariable[]{sSponge, sSponge});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockByNameSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_BY_NAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBlockByName() throws EvaluationException {\n        Operators.OBJECT_BLOCK_BY_NAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BLOCK_PROPERTIES -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockBlockProperties() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_PROPERTIES.evaluate(new IVariable[]{bCarrotGrown});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an nbt tag\");\n        CompoundTag tag = new CompoundTag();\n        tag.putString(\"age\", \"1\");\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), tag, \"blockproperties(minecraft:carrot) = {...}\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockPropertiesLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_PROPERTIES.evaluate(new IVariable[]{bLeaves, bLeaves});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockPropertiesSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_PROPERTIES.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBlockProperties() throws EvaluationException {\n        Operators.OBJECT_BLOCK_PROPERTIES.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BLOCK_WITH_PROPERTIES -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockBlockWithProperties() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_WITH_PROPERTIES.evaluate(new IVariable[]{bCarrot, nbtCarrotGrown});\n        Asserts.check(res1 instanceof ValueObjectTypeBlock.ValueBlock, \"result is a block\");\n        TestHelpers.assertEqual(((ValueObjectTypeBlock.ValueBlock) res1).getRawValue().get(), bCarrotGrown.getValue().getRawValue().get(), \"blockwithproperties(minecraft:carrot, ...) = {...}\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockWithPropertiesLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_WITH_PROPERTIES.evaluate(new IVariable[]{bLeaves, nbtCarrotGrown, bLeaves});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockWithPropertiesSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_WITH_PROPERTIES.evaluate(new IVariable[]{bLeaves});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBlockWithProperties() throws EvaluationException {\n        Operators.OBJECT_BLOCK_WITH_PROPERTIES.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BLOCK_POSSIBLE_PROPERTIES -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockBlockPossibleProperties() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_POSSIBLE_PROPERTIES.evaluate(new IVariable[]{bCarrotGrown});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an nbt tag\");\n        CompoundTag tag = new CompoundTag();\n        ListTag list = new ListTag();\n        list.add(StringTag.valueOf(\"0\"));\n        list.add(StringTag.valueOf(\"1\"));\n        list.add(StringTag.valueOf(\"2\"));\n        list.add(StringTag.valueOf(\"3\"));\n        list.add(StringTag.valueOf(\"4\"));\n        list.add(StringTag.valueOf(\"5\"));\n        list.add(StringTag.valueOf(\"6\"));\n        list.add(StringTag.valueOf(\"7\"));\n        tag.put(\"age\", list);\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), tag, \"blockpossibleproperties(minecraft:carrot) = {...}\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockPossiblePropertiesLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_POSSIBLE_PROPERTIES.evaluate(new IVariable[]{bLeaves, bLeaves});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockPossiblePropertiesSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_POSSIBLE_PROPERTIES.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBlockPossibleProperties() throws EvaluationException {\n        Operators.OBJECT_BLOCK_POSSIBLE_PROPERTIES.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TAG -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockTag() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_TAG.evaluate(new IVariable[]{bSand});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        int sandTagCount = (int) Blocks.SAND.defaultBlockState().getBlock().builtInRegistryHolder().tags().count();\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), sandTagCount, \"size(tag(sand)) = \" + sandTagCount);\n\n        IValue res2 = Operators.OBJECT_BLOCK_TAG.evaluate(new IVariable[]{bLeaves});\n        int leavesTagCount = (int) Blocks.OAK_LEAVES.defaultBlockState().getBlock().builtInRegistryHolder().tags().count();\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res2).getRawValue().getLength(), leavesTagCount, \"size(tag(leaves)) = \" + leavesTagCount);\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_TAG.evaluate(new IVariable[]{bLeaves, bLeaves});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_TAG.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTag() throws EvaluationException {\n        Operators.OBJECT_BLOCK_TAG.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TAG_STACKS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockTagStacks() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_BLOCK_TAG_STACKS.evaluate(new IVariable[]{sSand});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), (int) Helpers.getBlockTagValues(\"minecraft:sand\").count(), \"size(tag_stacks(sand))\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagStacksLarge() throws EvaluationException {\n        Operators.OBJECT_BLOCK_TAG_STACKS.evaluate(new IVariable[]{sSand, sSand});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagStacksSmall() throws EvaluationException {\n        Operators.OBJECT_BLOCK_TAG_STACKS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTagStacks() throws EvaluationException {\n        Operators.OBJECT_BLOCK_TAG_STACKS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestEntityOperators.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport com.mojang.authlib.GameProfile;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.server.level.ClientInformation;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.EquipmentSlot;\nimport net.minecraft.world.entity.animal.chicken.Chicken;\nimport net.minecraft.world.entity.animal.cow.Cow;\nimport net.minecraft.world.entity.animal.pig.Pig;\nimport net.minecraft.world.entity.animal.sheep.Sheep;\nimport net.minecraft.world.entity.decoration.ItemFrame;\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.entity.monster.zombie.Zombie;\nimport net.minecraft.world.entity.vehicle.boat.Boat;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\nimport java.util.UUID;\n\n/**\n * Test the different logical operators.\n * @author rubensworks\n */\npublic class TestEntityOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableEntity eZombie;\n    private DummyVariableEntity eZombieBurning;\n    private DummyVariableEntity eZombieWet;\n    private DummyVariableEntity eZombieCrouching;\n    private DummyVariableEntity eZombieEating;\n    private DummyVariableEntity eChicken;\n    private DummyVariableEntity eItem;\n    private DummyVariableEntity eItemFrame;\n    private DummyVariableEntity ePlayer;\n    private DummyVariableEntity eZombieHeldItems;\n    private DummyVariableEntity eBoat;\n    private DummyVariableEntity eItemframe;\n    private DummyVariableEntity eZombieAged;\n    private DummyVariableEntity eZombieBaby;\n    private DummyVariableEntity eCow;\n    private DummyVariableEntity eCowAlreadyBred;\n    private DummyVariableEntity eCowBaby;\n    private DummyVariableEntity eCowInLove;\n    private DummyVariableEntity ePig;\n    private DummyVariableEntity eSheep;\n    private DummyVariableEntity eSheepSheared;\n\n    private DummyVariableItemStack iCarrot;\n    private DummyVariableItemStack iWheat;\n\n    protected ValueObjectTypeEntity.ValueEntity makeEntity(Entity entity) {\n        return new ValueEntityMock(entity);\n    }\n\n    @IntegrationBefore\n    public void before() {\n        Level world = ServerLifecycleHooks.getCurrentServer().getLevel(Level.OVERWORLD);\n        eZombie = new DummyVariableEntity(makeEntity(new Zombie(world) {\n            @Override\n            public boolean isInWaterOrRain() {\n                return false;\n            }\n        }));\n        Zombie zombieBurning = new Zombie(world);\n        zombieBurning.setRemainingFireTicks(10);\n        eZombieBurning = new DummyVariableEntity(makeEntity(zombieBurning));\n        Zombie zombieWet = new Zombie(world) {\n            @Override\n            protected void registerGoals() {\n                super.registerGoals();\n                this.wasTouchingWater = true;\n            }\n        };\n        eZombieWet = new DummyVariableEntity(makeEntity(zombieWet));\n        Zombie zombieCrouching = new Zombie(world) {\n            @Override\n            public boolean isCrouching() {\n                return true;\n            }\n        };\n        eZombieCrouching = new DummyVariableEntity(makeEntity(zombieCrouching));\n        Zombie zombieEating = new Zombie(world) {\n            @Override\n            public int getUseItemRemainingTicks() {\n                return 1;\n            }\n        };\n        eZombieEating = new DummyVariableEntity(makeEntity(zombieEating));\n        eChicken = new DummyVariableEntity(makeEntity(new Chicken(EntityType.CHICKEN, world)));\n        eItem = new DummyVariableEntity(makeEntity(new ItemEntity(world, 0, 0, 0, ItemStack.EMPTY)));\n        eItemFrame = new DummyVariableEntity(makeEntity(new ItemFrame(world, new BlockPos(0, 0, 0), Direction.NORTH)));\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            ePlayer = new DummyVariableEntity(makeEntity(world.players().get(0)));\n        } else {\n            ePlayer = new DummyVariableEntity(makeEntity(new ServerPlayer(\n                    ServerLifecycleHooks.getCurrentServer(),\n                    ServerLifecycleHooks.getCurrentServer().overworld(),\n                    new GameProfile(UUID.randomUUID(), \"test\"),\n                    ClientInformation.createDefault()\n            )));\n        }\n        Zombie zombieHeldItems = new Zombie(world);\n        zombieHeldItems.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.APPLE));\n        zombieHeldItems.setItemSlot(EquipmentSlot.OFFHAND, new ItemStack(Items.POTATO));\n        eZombieHeldItems = new DummyVariableEntity(makeEntity(zombieHeldItems));\n        Boat boat = new Boat(EntityType.ACACIA_BOAT, world, () -> ItemStack.EMPTY.getItem());\n        eZombie.getValue().getRawValue().get().startRiding(boat, true, true);\n        eBoat = new DummyVariableEntity(makeEntity(boat));\n        ItemFrame itemframe = new ItemFrame(world, new BlockPos(0, 0, 0), Direction.NORTH);\n        itemframe.setItem(new ItemStack(Items.POTATO));\n        itemframe.setRotation(3);\n        eItemframe = new DummyVariableEntity(makeEntity(itemframe));\n        Zombie zombieAged = new Zombie(world) {\n            @Override\n            public int getNoActionTime() {\n                return 3;\n            }\n        };\n        eZombieAged = new DummyVariableEntity(makeEntity(zombieAged));\n        Zombie zombieBaby = new Zombie(world);\n        zombieBaby.setBaby(true);\n        eZombieBaby = new DummyVariableEntity(makeEntity(zombieBaby));\n        eCow = new DummyVariableEntity(makeEntity(new Cow(EntityType.COW, world)));\n        eCowAlreadyBred = new DummyVariableEntity(makeEntity(new Cow(EntityType.COW, world) {\n            @Override\n            public int getAge() {\n                return 10;\n            }\n        }));\n        eCowBaby = new DummyVariableEntity(makeEntity(new Cow(EntityType.COW, world) {\n            @Override\n            public int getAge() {\n                return -10;\n            }\n        }));\n        eCowInLove = new DummyVariableEntity(makeEntity(new Cow(EntityType.COW, world) {\n            @Override\n            public boolean isInLove() {\n                return true;\n            }\n        }));\n        ePig = new DummyVariableEntity(makeEntity(new Pig(EntityType.PIG, world)));\n        eSheep = new DummyVariableEntity(makeEntity(new Sheep(EntityType.SHEEP, world)));\n        Sheep sheepSheared = new Sheep(EntityType.SHEEP, world);\n        sheepSheared.setSheared(true);\n        eSheepSheared = new DummyVariableEntity(makeEntity(sheepSheared));\n\n        iCarrot = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.CARROT)));\n        iWheat = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.WHEAT)));\n    }\n\n    /**\n     * ----------------------------------- ISMOB -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsMob() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), true, \"isismob(zombie) = true\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{eChicken});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"isismob(chicken) = false\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), false, \"isismob(item) = false\");\n\n        IValue res4 = Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{eItemFrame});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), false, \"isismob(itemframe) = false\");\n\n        IValue res5 = Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{ePlayer});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), false, \"isismob(player) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsMobLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsMobSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsMob() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISMOB.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISANIMAL -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsAnimal() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isisanimal(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{eChicken});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isisanimal(chicken) = true\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), false, \"isisanimal(item) = false\");\n\n        IValue res4 = Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{eItemFrame});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), false, \"isisanimal(itemframe) = false\");\n\n        IValue res5 = Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{ePlayer});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), false, \"isismob(player) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsAnimalLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsAnimalSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsAnimal() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISANIMAL.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISITEM -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsItem() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isisitem(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{eChicken});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"isisitem(chicken) = false\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"isisitem(item) = true\");\n\n        IValue res4 = Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{eItemFrame});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), false, \"isisitem(itemframe) = false\");\n\n        IValue res5 = Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{ePlayer});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), false, \"isismob(player) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsItemLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsItemSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsItem() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISITEM.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISPLAYER -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsPlayer() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isisplayer(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{eChicken});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"isisplayer(chicken) = false\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), false, \"isisplayer(item) = false\");\n\n        IValue res4 = Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{eItemFrame});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), false, \"isisplayer(itemframe) = false\");\n\n        IValue res5 = Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{ePlayer});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), true, \"isisplayer(player) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsPlayerLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsPlayerSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsPlayer() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISPLAYER.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ITEMSTACK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockItemStack() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ITEMSTACK.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an itemstack\");\n        TestHelpers.assertEqual(!((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().isEmpty(), false, \"itemstack(zombie) = null\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ITEMSTACK.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res2).getRawValue().isEmpty(), true, \"itemstack(null) = null\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemStackLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ITEMSTACK.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemStackSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ITEMSTACK.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeItemStack() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ITEMSTACK.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HEALTH -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockHealth() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_HEALTH.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeDouble.ValueDouble, \"result is a double\");\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res1).getRawValue(), 20.0D, \"health(zombie) = 10\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_HEALTH.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res2).getRawValue(), 0D, \"health(item) = 0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHealthLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HEALTH.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHealthSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HEALTH.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHealth() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HEALTH.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WIDTH -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockWidth() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_WIDTH.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeDouble.ValueDouble, \"result is a double\");\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res1).getRawValue(), 0.6D, \"width(zombie) = 0.6\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_WIDTH.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res2).getRawValue(), 0.25D, \"width(item) = 0.25\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeWidthLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_WIDTH.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeWidthSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_WIDTH.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeWidth() throws EvaluationException {\n        Operators.OBJECT_ENTITY_WIDTH.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HEIGHT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockHeight() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_HEIGHT.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeDouble.ValueDouble, \"result is a double\");\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res1).getRawValue(), 2D, \"height(zombie) = 2\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_HEIGHT.evaluate(new IVariable[]{eItem});\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res2).getRawValue(), 0.25D, \"height(item) = 0.25\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHeightLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HEIGHT.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHeightSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HEIGHT.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHeight() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HEIGHT.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISBURNING -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsBurning() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISBURNING.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isburning(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISBURNING.evaluate(new IVariable[]{eZombieBurning});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isburning(zombie:burning) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsBurningLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISBURNING.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsBurningSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISBURNING.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsBurning() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISBURNING.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISWET -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsWet() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISWET.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"iswet(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISWET.evaluate(new IVariable[]{eZombieWet});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"iswet(zombie:wet) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsWetLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISWET.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsWetSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISWET.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsWet() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISWET.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISCROUCHING -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsCrouching() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISCROUCHING.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"issneaking(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISCROUCHING.evaluate(new IVariable[]{eZombieCrouching});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"issneaking(zombie:sneaking) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsCrouchingLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISCROUCHING.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsCrouchingSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISCROUCHING.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsCrouching() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISCROUCHING.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISEATING -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsEating() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISEATING.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"iseating(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISEATING.evaluate(new IVariable[]{eZombieEating});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"iseating(zombie:eating) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsEatingLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISEATING.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsEatingSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISEATING.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsEating() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISEATING.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MODNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityModName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_MODNAME.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"Minecraft\", \"modname(zombie) = Minecraft\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_MODNAME.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_MODNAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeModName() throws EvaluationException {\n        Operators.OBJECT_ENTITY_MODNAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HELDITEM_MAIN -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityHeldItemMain() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_HELDITEM_MAIN.evaluate(new IVariable[]{eZombieHeldItems});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an item\");\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().getItem(), Items.APPLE, \"helditemmain(zombie) = apple\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHeldItemMainLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HELDITEM_MAIN.evaluate(new IVariable[]{eZombieHeldItems, eZombieHeldItems});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHeldItemMainSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HELDITEM_MAIN.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHeldItemMain() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HELDITEM_MAIN.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HELDITEM_OFF -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityHeldItemOff() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_HELDITEM_OFF.evaluate(new IVariable[]{eZombieHeldItems});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an item\");\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().getItem(), Items.POTATO, \"helditemoff(zombie) = potato\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHeldItemOffLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HELDITEM_OFF.evaluate(new IVariable[]{eZombieHeldItems, eZombieHeldItems});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHeldItemOffSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HELDITEM_OFF.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHeldItemOff() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HELDITEM_OFF.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MOUNTED -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityMounted() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_MOUNTED.evaluate(new IVariable[]{eBoat});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 1, \"#mounted(boat) = 1\");\n        // We can not use the mocked entity value in a clean way here, checking the list size should be enough.\n        //TestHelpers.assertEqual(((ValueObjectTypeEntity.ValueEntity) ((ValueTypeList.ValueList) res1).getRawValue().get(0)).getRawValue().get(), eZombie.getValue().getRawValue().get(), \"mounted(boat)(0) = zombie\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeMountedLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_MOUNTED.evaluate(new IVariable[]{eBoat, eBoat});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeMountedSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_MOUNTED.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeMounted() throws EvaluationException {\n        Operators.OBJECT_ENTITY_MOUNTED.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ITEMFRAME_CONTENTS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityItemframeContents() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMFRAME_CONTENTS.evaluate(new IVariable[]{eItemframe});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an item\");\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().getItem(), Items.POTATO, \"itemframecontents(itemframe) = potato\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemframeContentsLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMFRAME_CONTENTS.evaluate(new IVariable[]{eItemframe, eItemframe});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemframeContentsSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMFRAME_CONTENTS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeItemframeContents() throws EvaluationException {\n        Operators.OBJECT_ITEMFRAME_CONTENTS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ITEMFRAME_ROTATION -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityItemframeRotation() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMFRAME_ROTATION.evaluate(new IVariable[]{eItemframe});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 3, \"itemframerotation(itemframe) = 3\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemframeRotationLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMFRAME_ROTATION.evaluate(new IVariable[]{eItemframe, eItemframe});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemframeRotationSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMFRAME_ROTATION.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeItemframeRotation() throws EvaluationException {\n        Operators.OBJECT_ITEMFRAME_ROTATION.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HURTSOUND -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityHurtSound() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_HURTSOUND.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"minecraft:entity.zombie.hurt\", \"hurtsound(zomie) = minecraft:entity.zombie.hurt\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHurtSoundLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HURTSOUND.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHurtSoundSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HURTSOUND.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHurtSound() throws EvaluationException {\n        Operators.OBJECT_ENTITY_HURTSOUND.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DEATHSOUND -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityDeathSound() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_DEATHSOUND.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"minecraft:entity.zombie.death\", \"deathsound(zomie) = minecraft:entity.zombie.death\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeDeathSoundLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_DEATHSOUND.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeDeathSoundSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_DEATHSOUND.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDeathSound() throws EvaluationException {\n        Operators.OBJECT_ENTITY_DEATHSOUND.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- AGE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockAge() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_AGE.evaluate(new IVariable[]{eZombieAged});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 3, \"age(zombie) = 3\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeAgeLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_AGE.evaluate(new IVariable[]{eZombieAged, eZombieAged});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeAgeSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_AGE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeAge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_AGE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISCHILD -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsChild() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISCHILD.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"ischild(zombie) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISCHILD.evaluate(new IVariable[]{eZombieBaby});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"ischild(zombie) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsChildLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISCHILD.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsChildSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISCHILD.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsChild() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISCHILD.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- CANBREED -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockCanBreed() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_CANBREED.evaluate(new IVariable[]{eCow});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), true, \"canbreed(cow) = true\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_CANBREED.evaluate(new IVariable[]{eCowAlreadyBred});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"canbreed(cowbred) = false\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_CANBREED.evaluate(new IVariable[]{eCowBaby});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), false, \"canbreed(cowbaby) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeCanBreedLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_CANBREED.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeCanBreedSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_CANBREED.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeCanBreed() throws EvaluationException {\n        Operators.OBJECT_ENTITY_CANBREED.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISINLOVE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsInLove() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISINLOVE.evaluate(new IVariable[]{eCow});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isinlove(cow) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISINLOVE.evaluate(new IVariable[]{eCowInLove});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isinlove(cowloving) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsInLoveLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISINLOVE.evaluate(new IVariable[]{eCow, eCow});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsInLoveSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISINLOVE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsInLove() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISINLOVE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- CANBREEDWITH -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockCanBreedWith() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{eCow, iCarrot});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"canbreedwith(cow, carrot) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{eCow, iWheat});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"canbreedwith(cow, wheat) = true\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{ePig, iCarrot});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"canbreedwith(pig, carrot) = true\");\n\n        IValue res4 = Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{ePig, iWheat});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), false, \"canbreedwith(pig, wheat) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeCanBreedWithLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{eCow, iCarrot, iCarrot});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeCanBreedWithSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{eCow});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeCanBreedWith() throws EvaluationException {\n        Operators.OBJECT_ENTITY_CANBREEDWITH.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISSHEARABLE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockIsShearable() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_ISSHEARABLE.evaluate(new IVariable[]{eCow});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isshearable(cow) = false\");\n\n        IValue res2 = Operators.OBJECT_ENTITY_ISSHEARABLE.evaluate(new IVariable[]{eSheep});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isshearable(sheep) = true\");\n\n        IValue res3 = Operators.OBJECT_ENTITY_ISSHEARABLE.evaluate(new IVariable[]{eSheepSheared});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), false, \"isshearable(sheepsheared) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsShearableLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISSHEARABLE.evaluate(new IVariable[]{eCow, eCow});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsShearableSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISSHEARABLE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsShearable() throws EvaluationException {\n        Operators.OBJECT_ENTITY_ISSHEARABLE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- NBT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlockNbt() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_NBT.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an nbt tag\");\n        TestHelpers.assertNonEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue(), new CompoundTag(), \"isnbt(zombie) is not null\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeNbtLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_NBT.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeNbtSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_NBT.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeNbt() throws EvaluationException {\n        Operators.OBJECT_ENTITY_NBT.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TYPE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEntityType() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ENTITY_TYPE.evaluate(new IVariable[]{eZombie});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"minecraft:zombie\", \"entitytype(zombie) = minecraft:zombie\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTypeLarge() throws EvaluationException {\n        Operators.OBJECT_ENTITY_TYPE.evaluate(new IVariable[]{eZombie, eZombie});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTypeSmall() throws EvaluationException {\n        Operators.OBJECT_ENTITY_TYPE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeType() throws EvaluationException {\n        Operators.OBJECT_ENTITY_TYPE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestFluidStackOperators.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.IntTag;\nimport net.minecraft.sounds.SoundEvents;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.item.Rarity;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.material.Fluids;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\n/**\n * Test the different logical operators.\n * @author rubensworks\n */\npublic class TestFluidStackOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableFluidStack eBucketLava;\n    private DummyVariableFluidStack eBucketWater;\n    private DummyVariableFluidStack eBucketEmpty;\n    private DummyVariableFluidStack eWater100;\n    private DummyVariableFluidStack eWater100Tag;\n    private DummyVariable<ValueTypeInteger.ValueInteger> i99;\n    private DummyVariable<ValueTypeString.ValueString> sDamage;\n    private DummyVariable<ValueTypeNbt.ValueNbt> t4;\n    private DummyVariable<ValueTypeString.ValueString> sWater;\n\n    @IntegrationBefore\n    public void before() {\n        eBucketLava = new DummyVariableFluidStack(ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.LAVA, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume())));\n        eBucketWater = new DummyVariableFluidStack(ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume())));\n        eBucketEmpty = new DummyVariableFluidStack(ValueObjectTypeFluidStack.ValueFluidStack.of(FluidStack.EMPTY));\n        eWater100 = new DummyVariableFluidStack(ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 100)));\n        eWater100Tag = new DummyVariableFluidStack(ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 100)));\n        eWater100Tag.getValue().getRawValue().set(DataComponents.DAMAGE, 3);\n        i99 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(99));\n        sDamage = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:damage\"));\n        t4 = new DummyVariable<>(ValueTypes.NBT, ValueTypeNbt.ValueNbt.of(IntTag.valueOf(4)));\n\n        i99 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(99));\n\n        sWater = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:water\"));\n\n        CompoundTag tag = new CompoundTag();\n        tag.putInt(\"Damage\", 51);\n    }\n\n    /**\n     * ----------------------------------- AMOUNT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testAmount() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_AMOUNT.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume(), \"amount(lava:1000) = 1000\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_AMOUNT.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume(), \"amount(water:1000) = 1000\");\n\n        IValue res3 = Operators.OBJECT_FLUIDSTACK_AMOUNT.evaluate(new IVariable[]{eWater100});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res3).getRawValue(), 100, \"amount(water:100) = 100\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeAmountLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_AMOUNT.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeAmountSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_AMOUNT.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeAmount() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_AMOUNT.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BLOCK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBlock() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_BLOCK.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueObjectTypeBlock.ValueBlock, \"result is a block\");\n        TestHelpers.assertEqual(((ValueObjectTypeBlock.ValueBlock) res1).getRawValue().get(), Blocks.LAVA.defaultBlockState(), \"block(lava) = lava\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_BLOCK.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueObjectTypeBlock.ValueBlock) res2).getRawValue().get(), Blocks.WATER.defaultBlockState(), \"block(water) = water\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BLOCK.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBlockSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BLOCK.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBlock() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BLOCK.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- LIGHT_LEVEL -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testLightLevel() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_LIGHT_LEVEL.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 15, \"lightLevel(lava) = 15\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_LIGHT_LEVEL.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 0, \"lightLevel(water) = 0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeLightLevelLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_LIGHT_LEVEL.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeLightLevelSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_LIGHT_LEVEL.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeLightLevel() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_LIGHT_LEVEL.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DENSITY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testDensity() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_DENSITY.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 3000, \"density(lava) = 1000\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_DENSITY.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 1000, \"density(water) = 1000\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeDensityLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DENSITY.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeDensitySmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DENSITY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDensity() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DENSITY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TEMPERATURE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testTemperature() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_TEMPERATURE.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 1300, \"temperature(lava) = 1300\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_TEMPERATURE.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 300, \"temperature(water) = 300\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTemperatureLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TEMPERATURE.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTemperatureSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TEMPERATURE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTemperature() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TEMPERATURE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- VISCOSITY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testViscosity() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_VISCOSITY.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 6000, \"viscosity(lava) = 6000\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_VISCOSITY.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 1000, \"viscosity(water) = 1000\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeViscosityLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_VISCOSITY.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeViscositySmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_VISCOSITY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeViscosity() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_VISCOSITY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- IS_LIGHTER_THAN_AIR -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testIsLighterThanAir() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isgaseous(lava) = false\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"isgaseous(water) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsLighterThanAirLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsLighterThanAirSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsLighterThanAir() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_IS_LIGHTER_THAN_AIR.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- RARITY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testRarity() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_RARITY.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), Rarity.COMMON.name(), \"rarity(lava) = common\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_RARITY.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), Rarity.COMMON.name(), \"rarity(water) = common\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeRarityLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_RARITY.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeRaritySmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_RARITY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeRarity() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_RARITY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- SOUND_BUCKET_EMPTY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testSoundBucketEmpty() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), SoundEvents.BUCKET_EMPTY_LAVA.location().toString(), \"soundBucketEmpty(lava) = bucket_empty\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), SoundEvents.BUCKET_EMPTY.location().toString(), \"soundBucketEmpty(water) = bucket_empty\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundBucketEmptyLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundBucketEmptySmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeSoundBucketEmpty() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_EMPTY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- SOUND_BUCKET_FILL -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testSoundBucketFill() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), SoundEvents.BUCKET_FILL_LAVA.location().toString(), \"soundBucketFill(lava) = bucket_fill\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), SoundEvents.BUCKET_FILL.location().toString(), \"soundBucketFill(water) = bucket_fill\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundBucketFillLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundBucketFillSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeSoundBucketFill() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_BUCKET_FILL.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- SOUND_FLUID_VAPORIZE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testSoundFluidVaporize() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"\", \"soundFluidVaporize(lava) = \");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), SoundEvents.FIRE_EXTINGUISH.location().toString(), \"soundFluidVaporize(water) = \");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundFluidVaporizeLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSoundFluidVaporizeSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeSoundFluidVaporize() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_SOUND_FLUID_VAPORIZE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BUCKET -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testBucket() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_BUCKET.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an item\");\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().getItem(), Items.LAVA_BUCKET, \"bucket(lava) = bucket_lava\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_BUCKET.evaluate(new IVariable[]{eBucketWater});\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res2).getRawValue().getItem(), Items.WATER_BUCKET, \"bucket(water) = bucket_water\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBucketLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BUCKET.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeBucketSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BUCKET.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBucket() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BUCKET.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISRAWFLUIDEQUAL -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testIsRawFluidEqual() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL.evaluate(new IVariable[]{eBucketLava, eBucketWater});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"israwfluidequal(lava, water) = false\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"israwfluidequal(lava:1000, lava:1000) = true\");\n\n        IValue res3 = Operators.OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL.evaluate(new IVariable[]{eBucketWater, eWater100});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"israwfluidequal(water:1000, water:100) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsRawFluidEqualLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL.evaluate(new IVariable[]{eBucketLava, eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsRawFluidEqualSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL.evaluate(new IVariable[]{eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsRawFluidEqual() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_ISRAWFLUIDEQUAL.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MODNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidModName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_MODNAME.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"Minecraft\", \"modname(lava) = Minecraft\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_MODNAME.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_MODNAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeModName() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_MODNAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DATA -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidNbt() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_DATA.evaluate(new IVariable[]{eBucketLava});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an nbt tag\");\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue().isPresent(), false, \"data(lava) = null\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_DATA.evaluate(new IVariable[]{eWater100Tag});\n        CompoundTag tag = new CompoundTag();\n        tag.putInt(\"minecraft:damage\", 3);\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), tag, \"nbt(watertag) != null\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeNbtLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA.evaluate(new IVariable[]{eBucketLava, eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeNbtSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeNbt() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_AMOUNT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithAmount() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_WITH_AMOUNT.evaluate(new IVariable[]{eBucketLava, i99});\n        Asserts.check(res1 instanceof ValueObjectTypeFluidStack.ValueFluidStack, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueObjectTypeFluidStack.ValueFluidStack) res1).getRawValue().getFluid(), eBucketLava.getValue().getRawValue().getFluid(), \"withamount(lava, 99) = lava@99\");\n        TestHelpers.assertEqual(((ValueObjectTypeFluidStack.ValueFluidStack) res1).getRawValue().getAmount(), 99, \"withamount(lava, 99) = lava@99\");\n\n        // Check if original amount is not changed\n        TestHelpers.assertEqual(eBucketLava.getValue().getRawValue().getAmount(), IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume(), \"original value is not changed\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeWithAmountLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_WITH_AMOUNT.evaluate(new IVariable[]{eBucketLava, i99, i99});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeWithAmountSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_WITH_AMOUNT.evaluate(new IVariable[]{eBucketLava});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeWithAmount() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_WITH_AMOUNT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DATA_KEYS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidStackDataKeys() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_DATA_KEYS.evaluate(new IVariable[]{eWater100});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList<?,?>, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<?,?>) res1).getRawValue().getLength(), 0, \"datakeys(water) = []\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_DATA_KEYS.evaluate(new IVariable[]{eWater100Tag});\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<?,?>) res2).getRawValue().getLength(), 1, \"datakeys(waterTag).length = 1\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(0))).getRawValue(), \"minecraft:damage\", \"datakeys(waterTag)[0] == ...\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataKeysDataKeysLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA_KEYS.evaluate(new IVariable[]{eWater100Tag, eWater100Tag});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataKeysDataKeysSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA_KEYS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDataKeys() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA_KEYS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DATA_VALUE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidStackDataValue() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_DATA_VALUE.evaluate(new IVariable[]{eWater100, sDamage});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an empty tag\");\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue().isEmpty(), true, \"datavalue(water, damage) = empty\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_DATA_VALUE.evaluate(new IVariable[]{eWater100Tag, sDamage});\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), IntTag.valueOf(3), \"datavalue(waterTag, damage).length = 3\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataValueDataValueLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA_VALUE.evaluate(new IVariable[]{eWater100Tag, sDamage, eWater100Tag});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataValueDataValueSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA_VALUE.evaluate(new IVariable[]{eWater100Tag});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDataValue() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_DATA_VALUE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_DATA -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidStackWithData() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_WITH_DATA.evaluate(new IVariable[]{eWater100, sDamage, t4});\n        Asserts.check(res1 instanceof ValueObjectTypeFluidStack.ValueFluidStack, \"result is an fluid\");\n        FluidStack outFluid1 = ((ValueObjectTypeFluidStack.ValueFluidStack) res1).getRawValue();\n        TestHelpers.assertEqual(outFluid1.getFluid(), eWater100.getValue().getRawValue().getFluid(), \"withdata(water, damage, 4) = water\");\n        TestHelpers.assertNonEqual(outFluid1.getComponents(), eWater100.getValue().getRawValue().getComponents(), \"withdata(water, damage, 4) !=components water\");\n        TestHelpers.assertEqual(outFluid1.get(DataComponents.DAMAGE), 4, \"withdata(water, damage, 4).damage = 4\");\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_WITH_DATA.evaluate(new IVariable[]{eWater100Tag, sDamage, t4});\n        FluidStack outFluid2 = ((ValueObjectTypeFluidStack.ValueFluidStack) res2).getRawValue();\n        TestHelpers.assertEqual(outFluid2.getFluid(), eWater100.getValue().getRawValue().getFluid(), \"withdata(waterTag, damage, 4) = water\");\n        TestHelpers.assertNonEqual(outFluid2.getComponents(), eWater100.getValue().getRawValue().getComponents(), \"withdata(waterTag, damage, 4) !=components water\");\n        TestHelpers.assertEqual(outFluid2.get(DataComponents.DAMAGE), 4, \"withdata(waterTag, damage, 4).damage = 4\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputWithDataWithDataLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_WITH_DATA.evaluate(new IVariable[]{eWater100Tag, sDamage, t4, eWater100Tag});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputWithDataWithDataSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_WITH_DATA.evaluate(new IVariable[]{eWater100Tag, sDamage});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeWithData() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_WITH_DATA.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TAG -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidStackTag() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_TAG.evaluate(new IVariable[]{eBucketWater});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        int waterTagCount = (int) Fluids.WATER.builtInRegistryHolder().tags().count();\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), waterTagCount, \"size(tag(water)) = \" + waterTagCount);\n\n        IValue res2 = Operators.OBJECT_FLUIDSTACK_TAG.evaluate(new IVariable[]{eBucketEmpty});\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res2).getRawValue().getLength(), 0, \"size(tag(empty)) = 0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TAG.evaluate(new IVariable[]{eBucketWater, eBucketWater});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TAG.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTag() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TAG.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TAG_STACKS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidStackTagStacks() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_TAG_STACKS.evaluate(new IVariable[]{sWater});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), (int) Helpers.getFluidTagValues(\"minecraft:water\").count(), \"size(tag_stacks(water))\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagStacksLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TAG_STACKS.evaluate(new IVariable[]{sWater, sWater});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagStacksSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TAG_STACKS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTagStacks() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_TAG_STACKS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- FLUIDBYNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluidFluidByName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_FLUIDSTACK_BY_NAME.evaluate(new IVariable[]{sWater});\n        Asserts.check(res1 instanceof ValueObjectTypeFluidStack.ValueFluidStack, \"result is a fluid\");\n        TestHelpers.assertEqual(((ValueObjectTypeFluidStack.ValueFluidStack) res1).getRawValue().getFluid(),\n                new FluidStack(Fluids.WATER, 1000).getFluid(), \"fluidbyname(minecraft:water) = water\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeFluidByNameLarge() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BY_NAME.evaluate(new IVariable[]{sWater, sWater});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeFluidByNameSmall() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BY_NAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeFluidByName() throws EvaluationException {\n        Operators.OBJECT_FLUIDSTACK_BY_NAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestIngredientsOperators.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.material.Fluids;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\n/**\n * Test the different ingredients operators.\n * @author rubensworks\n */\npublic class TestIngredientsOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariable<ValueTypeInteger.ValueInteger> i0;\n    private DummyVariable<ValueTypeInteger.ValueInteger> i1;\n    private DummyVariable<ValueTypeInteger.ValueInteger> i2;\n    private DummyVariable<ValueTypeInteger.ValueInteger> i3;\n\n    private DummyVariableIngredients iEmpty;\n    private DummyVariableIngredients iItems;\n    private DummyVariable<ValueTypeList.ValueList> lItems;\n    private DummyVariableIngredients iFluids;\n    private DummyVariable<ValueTypeList.ValueList> lFluids;\n    private DummyVariableIngredients iEnergies;\n    private DummyVariable<ValueTypeList.ValueList> lEnergies;\n    private IMixedIngredients inputIngredients;\n    private DummyVariableIngredients iMix;\n\n    private DummyVariable<ValueObjectTypeItemStack.ValueItemStack> iItem;\n    private Predicate<ValueObjectTypeItemStack.ValueItemStack> pItemRaw;\n    private DummyVariable<ValueTypeOperator.ValueOperator> pItem;\n\n    private DummyVariable<ValueObjectTypeFluidStack.ValueFluidStack> iFluid;\n    private Predicate<ValueObjectTypeFluidStack.ValueFluidStack> pFluidRaw;\n    private DummyVariable<ValueTypeOperator.ValueOperator> pFluid;\n\n    private DummyVariable<ValueTypeLong.ValueLong> iEnergy;\n    private Predicate<ValueTypeInteger.ValueInteger> pEnergyRaw;\n    private DummyVariable<ValueTypeOperator.ValueOperator> pEnergy;\n\n    @IntegrationBefore\n    public void before() {\n        i0 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(1));\n        i2 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(2));\n        i3 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(3));\n\n        iEmpty = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                new MixedIngredients(Maps.newIdentityHashMap())));\n\n        iItems = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                MixedIngredients.ofInstances(IngredientComponent.ITEMSTACK, Lists.newArrayList(\n                        ItemStack.EMPTY, new ItemStack(Items.OAK_BOAT), new ItemStack(Blocks.STONE), ItemStack.EMPTY)\n                )));\n        lItems = new DummyVariable<>(ValueTypes.LIST, ValueTypeList.ValueList.ofAll(\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY),\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.OAK_BOAT)),\n                ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Blocks.STONE)),\n                ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY)\n        ));\n\n        iFluids = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                MixedIngredients.ofInstances(IngredientComponent.FLUIDSTACK, Lists.newArrayList(\n                        new FluidStack(Fluids.LAVA, 1000), new FluidStack(Fluids.WATER, 125))\n                )));\n        lFluids = new DummyVariable<>(ValueTypes.LIST, ValueTypeList.ValueList.ofAll(\n                ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.LAVA, 1000)),\n                ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 125))\n        ));\n\n        iEnergies = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                MixedIngredients.ofInstances(IngredientComponent.ENERGY, Lists.newArrayList(\n                        666L, 777L, 0L)\n                )));\n        lEnergies = new DummyVariable<>(ValueTypes.LIST, ValueTypeList.ValueList.ofAll(\n                ValueTypeLong.ValueLong.of(666L),\n                ValueTypeLong.ValueLong.of(777L),\n                ValueTypeLong.ValueLong.of(0L)\n        ));\n\n        Map<IngredientComponent<?, ?>, List<?>> ingredients = Maps.newIdentityHashMap();\n        ingredients.put(IngredientComponent.ENERGY, Lists.newArrayList(777L));\n        ingredients.put(IngredientComponent.FLUIDSTACK, Lists.newArrayList(new FluidStack(Fluids.WATER, 125)));\n        ingredients.put(IngredientComponent.ITEMSTACK, Lists.newArrayList(new ItemStack(Items.OAK_BOAT), new ItemStack(Item.byBlock(Blocks.STONE))));\n        inputIngredients = new MixedIngredients(ingredients);\n        iMix = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(inputIngredients));\n\n        iItem = new DummyVariable<>(ValueTypes.OBJECT_ITEMSTACK, ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.APPLE)));\n        iFluid = new DummyVariable<>(ValueTypes.OBJECT_FLUIDSTACK, ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 123)));\n        iEnergy = new DummyVariable<>(ValueTypes.LONG, ValueTypeLong.ValueLong.of(123L));\n    }\n\n    /**\n     * ----------------------------------- ITEMS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItems() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_ITEMS.evaluate(new IVariable[]{iMix});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 2, \"items(mix, 0).size = 2\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack>) res1)\n                .getRawValue().get(0).getRawValue().getItem(), Items.OAK_BOAT, \"items(mix, 0) = boat\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<ValueObjectTypeItemStack, ValueObjectTypeItemStack.ValueItemStack>) res1)\n                .getRawValue().get(1).getRawValue().getItem(), Item.byBlock(Blocks.STONE), \"items(mix, 0) = boat\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testItemsSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_ITEMS.evaluate(new IVariable[]{iMix, i0});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testItemsSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_ITEMS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testItemsSize() throws EvaluationException {\n        Operators.INGREDIENTS_ITEMS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- FLUIDS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testFluids() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_FLUIDS.evaluate(new IVariable[]{iFluids});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 2, \"fluids(fluids, 0).size = 2\");\n        TestHelpers.assertEqual(res1,\n                ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_FLUIDSTACK, Lists.newArrayList(\n                        ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.LAVA, 1000)),\n                        ValueObjectTypeFluidStack.ValueFluidStack.of(new FluidStack(Fluids.WATER, 125))\n                )), \"fluids(fluids) = lava, water\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testFluidsSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_FLUIDS.evaluate(new IVariable[]{iMix, i0});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testFluidsSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_FLUIDS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testFluidsSize() throws EvaluationException {\n        Operators.INGREDIENTS_FLUIDS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ENERGIES -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testEnergies() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_ENERGIES.evaluate(new IVariable[]{iEnergies});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 3, \"energys(energys, 0).size = 1\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<ValueTypeLong, ValueTypeLong.ValueLong>) res1)\n                .getRawValue().get(0).getRawValue(), 666L, \"energies(energies, 0) = 666\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<ValueTypeLong, ValueTypeLong.ValueLong>) res1)\n                .getRawValue().get(1).getRawValue(), 777L, \"energies(energies, 0) = 777\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<ValueTypeLong, ValueTypeLong.ValueLong>) res1)\n                .getRawValue().get(2).getRawValue(), 0L, \"energies(energies, 0) = 0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testEnergiesSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_ENERGIES.evaluate(new IVariable[]{iMix, i0});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testEnergiesSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_ENERGIES.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testEnergiesSize() throws EvaluationException {\n        Operators.INGREDIENTS_ENERGIES.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_ITEM -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithItem() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_WITH_ITEM.evaluate(new IVariable[]{iMix, i0, iItem});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredient\");\n        IMixedIngredients outputIngredients1 = ((ValueObjectTypeIngredients.ValueIngredients) res1).getRawValue().get();\n        List<ItemStack> outputList1 = outputIngredients1.getInstances(IngredientComponent.ITEMSTACK);\n        TestHelpers.assertEqual(outputList1.size(), 2, \"with_items(mix, 0, items)[0]size = 2\");\n        TestHelpers.assertEqual(outputList1.get(0).getItem(), Items.APPLE,\n                \"with_items(mix, 0, items)[0] = items[0]\");\n        TestHelpers.assertEqual(outputList1.get(1).getItem(), Item.byBlock(Blocks.STONE),\n                \"with_items(mix, 0, items)[1] = items[1]\");\n\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK).size(), inputIngredients.getInstances(IngredientComponent.ITEMSTACK).size(), \"Items size remains the same\");\n        TestHelpers.assertNonEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK).get(0), inputIngredients.getInstances(IngredientComponent.ITEMSTACK).get(0), \"Items 0 changes\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK).get(1), inputIngredients.getInstances(IngredientComponent.ITEMSTACK).get(1), \"Items 1 remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK), \"Fluid remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ENERGY), inputIngredients.getInstances(IngredientComponent.ENERGY), \"Energy remains the same\");\n\n\n        IValue res2 = Operators.INGREDIENTS_WITH_ITEM.evaluate(new IVariable[]{iMix, i2, iItem});\n        IMixedIngredients outputIngredients2 = ((ValueObjectTypeIngredients.ValueIngredients) res2).getRawValue().get();\n        List<ItemStack> outputList2 = outputIngredients2.getInstances(IngredientComponent.ITEMSTACK);\n        TestHelpers.assertEqual(outputList2.size(), 3, \"with_items(mix, 2, items)[0]size = 3\");\n        TestHelpers.assertEqual(outputList2.get(0).getItem(), Items.OAK_BOAT,\n                \"with_items(mix, 2, items)[0] = items[0]\");\n        TestHelpers.assertEqual(outputList2.get(1).getItem(), Item.byBlock(Blocks.STONE),\n                \"with_items(mix, 2, items)[1] = items[1]\");\n        TestHelpers.assertEqual(outputList2.get(2).getItem(), Items.APPLE,\n                \"with_items(mix, 2, items)[2] = items[2]\");\n\n        TestHelpers.assertNonEqual(outputIngredients2.getInstances(IngredientComponent.ITEMSTACK).size(), inputIngredients.getInstances(IngredientComponent.ITEMSTACK).size(), \"Items size changes\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.ITEMSTACK).get(0), inputIngredients.getInstances(IngredientComponent.ITEMSTACK).get(0), \"Items 0 remains the same\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.ITEMSTACK).get(1), inputIngredients.getInstances(IngredientComponent.ITEMSTACK).get(1), \"Items 1 remains the same\");\n        TestHelpers.assertNonEqual(outputIngredients2.getInstances(IngredientComponent.ITEMSTACK).get(2), Collections.emptyList(), \"Items 3 is not empty\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.FLUIDSTACK), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK), \"Fluid remains the same\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.ENERGY), inputIngredients.getInstances(IngredientComponent.ENERGY), \"Energy remains the same\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithItemSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ITEM.evaluate(new IVariable[]{iMix, i0, iItem});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithItemSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ITEM.evaluate(new IVariable[]{iMix, i0});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithItemSize() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ITEM.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_FLUID -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithFluid() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_WITH_FLUID.evaluate(new IVariable[]{iMix, i0, iFluid});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredient\");\n        IMixedIngredients outputIngredients1 = ((ValueObjectTypeIngredients.ValueIngredients) res1).getRawValue().get();\n        List<FluidStack> outputList1 = outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK);\n        TestHelpers.assertEqual(outputList1.size(), 1, \"with_fluids(mix, 0, fluids)[0]size = 1\");\n        TestHelpers.assertEqual(FluidStack.matches(outputList1.get(0), new FluidStack(Fluids.WATER, 123)), true,\n                \"with_fluids(mix, 0, fluids)[0] = fluids[0]\");\n\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK).size(), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK).size(), \"Fluids size remains the same\");\n        TestHelpers.assertNonEqual(ValueObjectTypeFluidStack.ValueFluidStack.of(outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK).get(0)), ValueObjectTypeFluidStack.ValueFluidStack.of(inputIngredients.getInstances(IngredientComponent.FLUIDSTACK).get(0)), \"Fluids 0 changes\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK), inputIngredients.getInstances(IngredientComponent.ITEMSTACK), \"Items remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ENERGY), inputIngredients.getInstances(IngredientComponent.ENERGY), \"Energy remains the same\");\n\n\n        IValue res2 = Operators.INGREDIENTS_WITH_FLUID.evaluate(new IVariable[]{iMix, i2, iFluid});\n        IMixedIngredients outputIngredients2 = ((ValueObjectTypeIngredients.ValueIngredients) res2).getRawValue().get();\n        List<FluidStack> outputList2 = outputIngredients2.getInstances(IngredientComponent.FLUIDSTACK);\n        TestHelpers.assertEqual(outputList2.size(), 3, \"with_fluids(mix, 3, fluids)[0]size = 2\");\n        TestHelpers.assertEqual(FluidStack.matches(outputList2.get(0), new FluidStack(Fluids.WATER, 125)), true,\n                \"with_fluids(mix, 2, fluids)[0] = fluids[0]\");\n        TestHelpers.assertEqual(FluidStack.matches(outputList2.get(1), FluidStack.EMPTY), true,\n                \"with_fluids(mix, 2, fluids)[1] = fluids[1]\");\n        TestHelpers.assertEqual(FluidStack.matches(outputList2.get(2), new FluidStack(Fluids.WATER, 123)), true,\n                \"with_fluids(mix, 2, fluids)[2] = fluids[2]\");\n\n        TestHelpers.assertNonEqual(outputIngredients2.getInstances(IngredientComponent.FLUIDSTACK).size(), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK).size(), \"Fluids size changes\");\n        TestHelpers.assertNonEqual(outputIngredients2.getInstances(IngredientComponent.FLUIDSTACK).get(2), Collections.emptyList(), \"Fluids 3 is not empty\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.ITEMSTACK), inputIngredients.getInstances(IngredientComponent.ITEMSTACK), \"Items remains the same\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.ENERGY), inputIngredients.getInstances(IngredientComponent.ENERGY), \"Energy remains the same\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithFluidSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_FLUID.evaluate(new IVariable[]{iMix, i0, iFluid});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithFluidSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_FLUID.evaluate(new IVariable[]{iMix, i0});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithFluidSize() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_FLUID.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_ENERGY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithEnergy() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_WITH_ENERGY.evaluate(new IVariable[]{iMix, i0, iEnergy});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredient\");\n        IMixedIngredients outputIngredients1 = ((ValueObjectTypeIngredients.ValueIngredients) res1).getRawValue().get();\n        List<Long> outputList1 = outputIngredients1.getInstances(IngredientComponent.ENERGY);\n        TestHelpers.assertEqual(outputList1.size(), 1, \"with_energy(mix, 0, energy)[0]size = 2\");\n        TestHelpers.assertEqual(outputList1.get(0), 123L, \"with_energy(mix, 0, energy)[0][0] = energy[0]\");\n\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ENERGY).size(), inputIngredients.getInstances(IngredientComponent.ENERGY).size(), \"Energies size remains the same\");\n        TestHelpers.assertNonEqual(outputIngredients1.getInstances(IngredientComponent.ENERGY).get(0), inputIngredients.getInstances(IngredientComponent.ENERGY).get(0), \"Energies 0 changes\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK), inputIngredients.getInstances(IngredientComponent.ITEMSTACK), \"Items remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK), \"Fluids remains the same\");\n\n\n        IValue res2 = Operators.INGREDIENTS_WITH_ENERGY.evaluate(new IVariable[]{iMix, i2, iEnergy});\n        IMixedIngredients outputIngredients2 = ((ValueObjectTypeIngredients.ValueIngredients) res2).getRawValue().get();\n        List<Long> outputList2 = outputIngredients2.getInstances(IngredientComponent.ENERGY);\n        TestHelpers.assertEqual(outputList2.size(), 3, \"with_energy(mix, 2, energy)[0]size = 3\");\n        TestHelpers.assertEqual(outputList2.get(0), 777L, \"with_energy(mix, 2, energy)[3][0] = energy[0]\");\n        TestHelpers.assertEqual(outputList2.get(1), 0L, \"with_energy(mix, 2, energy)[3][0] = energy[0]\");\n        TestHelpers.assertEqual(outputList2.get(2), 123L, \"with_energy(mix, 2, energy)[3][0] = energy[0]\");\n\n        TestHelpers.assertNonEqual(outputIngredients2.getInstances(IngredientComponent.ENERGY).size(), inputIngredients.getInstances(IngredientComponent.ENERGY).size(), \"Energies size changes\");\n        TestHelpers.assertNonEqual(outputIngredients2.getInstances(IngredientComponent.ENERGY).get(2), Collections.emptyList(), \"Energies 3 is not empty\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.ITEMSTACK), inputIngredients.getInstances(IngredientComponent.ITEMSTACK), \"Items remains the same\");\n        TestHelpers.assertEqual(outputIngredients2.getInstances(IngredientComponent.FLUIDSTACK), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK), \"Fluids remains the same\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithEnergySizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ENERGY.evaluate(new IVariable[]{iMix, i0, iEnergy});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithEnergySizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ENERGY.evaluate(new IVariable[]{iMix, i0});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithEnergySize() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ENERGY.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_ITEMS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithItems() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_WITH_ITEMS.evaluate(new IVariable[]{iMix, lItems});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredient\");\n        IMixedIngredients outputIngredients1 = ((ValueObjectTypeIngredients.ValueIngredients) res1).getRawValue().get();\n        List<ItemStack> outputList1 = outputIngredients1.getInstances(IngredientComponent.ITEMSTACK);\n        TestHelpers.assertEqual(outputList1.size(), 4, \"with_items(mix, items)[0]size = 4\");\n        TestHelpers.assertEqual(outputList1.get(0).getItem(), Items.AIR,\n                \"with_items(mix, items)[0] = items[0]\");\n        TestHelpers.assertEqual(outputList1.get(1).getItem(), Items.OAK_BOAT,\n                \"with_items(mix, items)[1] = items[1]\");\n        TestHelpers.assertEqual(outputList1.get(2).getItem(), Item.byBlock(Blocks.STONE),\n                \"with_items(mix, items)[2] = items[2]\");\n        TestHelpers.assertEqual(outputList1.get(3).getItem(), Items.AIR,\n                \"with_items(mix, items)[3] = items[3]\");\n\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK), \"Fluid remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ENERGY), inputIngredients.getInstances(IngredientComponent.ENERGY), \"Energy remains the same\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithItemsSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ITEMS.evaluate(new IVariable[]{iMix, lItems});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithItemsSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ITEMS.evaluate(new IVariable[]{iMix});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithItemsSize() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ITEMS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_FLUIDS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithFluids() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_WITH_FLUIDS.evaluate(new IVariable[]{iMix, lFluids});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredient\");\n        IMixedIngredients outputIngredients1 = ((ValueObjectTypeIngredients.ValueIngredients) res1).getRawValue().get();\n        List<FluidStack> outputList1 = outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK);\n        TestHelpers.assertEqual(outputList1.size(), 2, \"with_fluids(mix, fluids)[0]size = 2\");\n        TestHelpers.assertEqual(FluidStack.matches(outputList1.get(0), new FluidStack(Fluids.LAVA, 1000)), true,\n                \"with_fluids(mix, fluids)[0] = fluids[0]\");\n        TestHelpers.assertEqual(FluidStack.matches(outputList1.get(1), new FluidStack(Fluids.WATER, 125)), true,\n                \"with_fluids(mix, fluids)[1] = fluids[1]\");\n\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK), inputIngredients.getInstances(IngredientComponent.ITEMSTACK), \"Item remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ENERGY), inputIngredients.getInstances(IngredientComponent.ENERGY), \"Energy remains the same\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithFluidsSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_FLUIDS.evaluate(new IVariable[]{iMix, lFluids});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithFluidsSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_FLUIDS.evaluate(new IVariable[]{iMix});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithFluidsSize() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_FLUIDS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_ENERGIES -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithEnergies() throws EvaluationException {\n        IValue res1 = Operators.INGREDIENTS_WITH_ENERGIES.evaluate(new IVariable[]{iMix, lEnergies});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredient\");\n        IMixedIngredients outputIngredients1 = ((ValueObjectTypeIngredients.ValueIngredients) res1).getRawValue().get();\n        List<Long> outputList1 = outputIngredients1.getInstances(IngredientComponent.ENERGY);\n        TestHelpers.assertEqual(outputList1.size(), 3, \"with_energies(mix, energies)[0]size = 3\");\n        TestHelpers.assertEqual(outputList1.get(0), 666L,\n                \"with_energies(mix, energies)[0] = energies[0]\");\n        TestHelpers.assertEqual(outputList1.get(1), 777L,\n                \"with_energies(mix, energies)[1] = energies[1]\");\n        TestHelpers.assertEqual(outputList1.get(2), 0L,\n                \"with_energies(mix, energies)[2] = energies[2]\");\n\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.ITEMSTACK), inputIngredients.getInstances(IngredientComponent.ITEMSTACK), \"Item remains the same\");\n        TestHelpers.assertEqual(outputIngredients1.getInstances(IngredientComponent.FLUIDSTACK), inputIngredients.getInstances(IngredientComponent.FLUIDSTACK), \"Fluid remains the same\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithEnergiesSizeLarge() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ENERGIES.evaluate(new IVariable[]{iMix, lEnergies});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithEnergiesSizeSmall() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ENERGIES.evaluate(new IVariable[]{iMix});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithEnergiesSize() throws EvaluationException {\n        Operators.INGREDIENTS_WITH_ENERGIES.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestItemStackOperators.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.IntTag;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.item.Rarity;\nimport net.minecraft.world.item.component.OminousBottleAmplifier;\nimport net.minecraft.world.item.enchantment.Enchantments;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.LeavesBlock;\nimport net.minecraft.world.level.material.Fluids;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.cyclops.cyclopscore.helper.EnchantmentHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryBase;\nimport org.cyclops.integrateddynamics.block.BlockEnergyBatteryConfig;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\n/**\n * Test the different logical operators.\n * @author rubensworks\n */\npublic class TestItemStackOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableItemStack iApple;\n    private DummyVariableItemStack iAppleNoData;\n    private DummyVariableItemStack iApple2;\n    private DummyVariableItemStack iAppleTag;\n    private DummyVariableItemStack iBeef;\n    private DummyVariableItemStack iEnderPearl;\n    private DummyVariableItemStack iHoe;\n    private DummyVariableItemStack iHoe100;\n    private DummyVariableItemStack iHoeEnchanted;\n    private DummyVariableItemStack iPickaxe;\n    private DummyVariableItemStack iStone;\n    private DummyVariableItemStack iDarkOakLeaves;\n    private DummyVariableItemStack iBucketLava;\n    private DummyVariableItemStack iWrench;\n    private DummyVariableItemStack iEnergyBatteryEmpty;\n    private DummyVariableItemStack iEnergyBatteryFull;\n    private DummyVariableItemStack iIronOre;\n    private DummyVariableItemStack iShulkerBox;\n    private DummyVariableItemStack iSeedWheat;\n    private DummyVariableItemStack iEmpty;\n\n    private DummyVariableBlock bStone;\n    private DummyVariableBlock bObsidian;\n\n    private DummyVariable<ValueTypeString.ValueString> sPlankWood;\n    private DummyVariable<ValueTypeString.ValueString> sMaxStackSize;\n\n    private DummyVariable<ValueTypeInteger.ValueInteger> int100;\n    private DummyVariable<ValueTypeInteger.ValueInteger> int200;\n\n    private DummyVariable<ValueTypeString.ValueString> sApple;\n\n    private DummyVariable<ValueTypeList.ValueList> lApples;\n\n    private DummyVariable<ValueTypeNbt.ValueNbt> t4;\n\n    @IntegrationBefore\n    public void before() {\n        iApple = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.APPLE)));\n        iAppleNoData = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.APPLE)));\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.MAX_STACK_SIZE);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.LORE);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.ENCHANTMENTS);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.REPAIR_COST);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.ATTRIBUTE_MODIFIERS);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.RARITY);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.FOOD);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.ITEM_NAME);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.ITEM_MODEL);\n        iAppleNoData.getValue().getRawValue().remove(DataComponents.CONSUMABLE);\n        iApple2 = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.APPLE, 2)));\n        ItemStack appleStack = new ItemStack(Items.APPLE);\n        appleStack.set(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, new OminousBottleAmplifier(2));\n        iAppleTag = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(appleStack));\n        iBeef = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.RED_BED)));\n        iEnderPearl = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.ENDER_PEARL)));\n        iHoe = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.DIAMOND_HOE)));\n        iHoe100 = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.DIAMOND_HOE)));\n        iHoe100.getValue().getRawValue().setDamageValue(100);\n        ItemStack hoeEnchanted = new ItemStack(Items.DIAMOND_HOE);\n        EnchantmentHelpers.setEnchantmentLevel(hoeEnchanted, ServerLifecycleHooks.getCurrentServer().registryAccess().holderOrThrow(Enchantments.AQUA_AFFINITY), 1);\n        hoeEnchanted.set(DataComponents.REPAIR_COST, 10);\n        iHoeEnchanted = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(hoeEnchanted));\n        iPickaxe = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.DIAMOND_PICKAXE)));\n        iStone = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Blocks.STONE)));\n        iDarkOakLeaves = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Blocks.DARK_OAK_LEAVES, 1)));\n        iBucketLava = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.LAVA_BUCKET)));\n        iWrench = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(RegistryEntries.ITEM_WRENCH)));\n        iEnergyBatteryEmpty = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY)));\n        ItemStack energyBatteryFull = new ItemStack(RegistryEntries.ITEM_ENERGY_BATTERY);\n        EnergyHandler energyStorage = energyBatteryFull.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(energyBatteryFull));\n        BlockEnergyBatteryBase.fill(energyStorage);\n        iEnergyBatteryFull = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(energyBatteryFull));\n        iIronOre = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Blocks.IRON_ORE)));\n        ItemStack shulkerBox = new ItemStack(Blocks.BLACK_SHULKER_BOX);\n        ResourceHandler<ItemResource> itemHandler = shulkerBox.getCapability(Capabilities.Item.ITEM, ItemAccess.forStack(shulkerBox));\n        try (var tx = Transaction.openRoot()) {\n            itemHandler.insert(0, ItemResource.of(Items.APPLE), 1, tx);\n            itemHandler.insert(10, ItemResource.of(Items.APPLE), 10, tx);\n            tx.commit();\n        }\n        iShulkerBox = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(shulkerBox));\n        iSeedWheat = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(new ItemStack(Items.WHEAT_SEEDS)));\n        iEmpty = new DummyVariableItemStack(ValueObjectTypeItemStack.ValueItemStack.of(ItemStack.EMPTY));\n\n        bStone = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.STONE.defaultBlockState()));\n        bObsidian = new DummyVariableBlock(ValueObjectTypeBlock.ValueBlock.of(Blocks.OBSIDIAN.defaultBlockState()));\n\n        sPlankWood = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:planks\"));\n        sMaxStackSize = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:max_stack_size\"));\n\n        int100 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(100));\n        int200 = new DummyVariable<>(ValueTypes.INTEGER, ValueTypeInteger.ValueInteger.of(200));\n\n        sApple = new DummyVariable<>(ValueTypes.STRING, ValueTypeString.ValueString.of(\"minecraft:apple\"));\n\n        lApples = new DummyVariable<>(ValueTypes.LIST, ValueTypeList.ValueList.ofAll(\n                iApple.getValue(),\n                iApple2.getValue(),\n                iIronOre.getValue(),\n                iApple.getValue(),\n                iApple.getValue(),\n                iHoe.getValue(),\n                iHoe100.getValue(),\n                iApple2.getValue()\n        ));\n\n        t4 = new DummyVariable<>(ValueTypes.NBT, ValueTypeNbt.ValueNbt.of(IntTag.valueOf(4)));\n    }\n\n    /**\n     * ----------------------------------- SIZE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackSize() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_SIZE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 1, \"size(apple:1) = 1\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_SIZE.evaluate(new IVariable[]{iApple2});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 2, \"size(apple:2) = 2\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSizeLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_SIZE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeSizeSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_SIZE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeSize() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_SIZE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MAXSIZE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackMaxSize() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_MAXSIZE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 64, \"maxsize(apple) = 64\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_MAXSIZE.evaluate(new IVariable[]{iEnderPearl});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 16, \"maxsize(enderpearl) = 16\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputMaxSizeMaxSizeLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MAXSIZE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputMaxSizeMaxSizeSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MAXSIZE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeMaxSize() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MAXSIZE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISSTACKABLE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsStackable() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISSTACKABLE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), true, \"isstackable(apple) = true\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISSTACKABLE.evaluate(new IVariable[]{iHoe});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"isstackable(hoe) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsStackableIsStackableLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISSTACKABLE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsStackableIsStackableSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISSTACKABLE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsStackable() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISSTACKABLE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISDAMAGEABLE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsDamageable() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISDAMAGEABLE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isdamageable(apple) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISDAMAGEABLE.evaluate(new IVariable[]{iHoe});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isdamageable(hoe) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsDamageableIsDamageableLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISDAMAGEABLE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsDamageableIsDamageableSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISDAMAGEABLE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsDamageable() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISDAMAGEABLE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DAMAGE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackDamage() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_DAMAGE.evaluate(new IVariable[]{iHoe});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 0, \"damage(hoe:0) = 0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_DAMAGE.evaluate(new IVariable[]{iHoe100});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 100, \"damage(hoe:100) = 100\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDamageDamageLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DAMAGE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDamageDamageSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DAMAGE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDamage() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DAMAGE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MAXDAMAGE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackMaxDamage() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_MAXDAMAGE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 0, \"maxdamage(apple) = 0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_MAXDAMAGE.evaluate(new IVariable[]{iHoe});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 1561, \"maxdamage(hoe) = 1561\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputMaxDamageMaxDamageLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MAXDAMAGE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputMaxDamageMaxDamageSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MAXDAMAGE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeMaxDamage() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MAXDAMAGE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISENCHANTED -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsEnchanted() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISENCHANTED.evaluate(new IVariable[]{iHoe});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isenchanted(hoe) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISENCHANTED.evaluate(new IVariable[]{iHoeEnchanted});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isenchanted(hoeenchanted) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsEnchantedIsEnchantedLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENCHANTED.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsEnchantedIsEnchantedSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENCHANTED.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsEnchanted() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENCHANTED.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISENCHANTABLE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsEnchantable() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISENCHANTABLE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isenchantable(apple) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISENCHANTABLE.evaluate(new IVariable[]{iHoe});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isenchantable(hoe) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsEnchantableIsEnchantableLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENCHANTABLE.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsEnchantableIsEnchantableSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENCHANTABLE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsEnchantable() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENCHANTABLE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n\n    /**\n     * ----------------------------------- REPAIRCOST -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackRepairCost() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_REPAIRCOST.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 0, \"repaircost(apple) = 0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_REPAIRCOST.evaluate(new IVariable[]{iHoeEnchanted});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 10, \"repaircost(hoe:10) = 10\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputRepairCostRepairCostLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_REPAIRCOST.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputRepairCostRepairCostSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_REPAIRCOST.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeRepairCost() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_REPAIRCOST.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- RARITY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackRarity() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_RARITY.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), Rarity.COMMON.name(), \"rarity(apple) = common\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_RARITY.evaluate(new IVariable[]{iHoeEnchanted});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), Rarity.RARE.name(), \"rarity(hoeenchanted) = rare\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputRarityRarityLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_RARITY.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputRarityRaritySmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_RARITY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeRarity() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_RARITY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- STRENGTH_VS_BLOCK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackStrengthVsBlock() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK.evaluate(new IVariable[]{iHoe, bStone});\n        Asserts.check(res1 instanceof ValueTypeDouble.ValueDouble, \"result is a double\");\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res1).getRawValue(), 1.0D, \"strengthvsblock(hoe, stone) = 1.0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK.evaluate(new IVariable[]{iPickaxe, bStone});\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res2).getRawValue(), 8.0D, \"strengthvsblock(pickaxe, stone) = 8.0\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK.evaluate(new IVariable[]{iPickaxe, bObsidian});\n        TestHelpers.assertEqual(((ValueTypeDouble.ValueDouble) res3).getRawValue(), 8.0D, \"strengthvsblock(pickaxe, obsidian) = 8.0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputStrengthVsBlockStrengthVsBlockLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK.evaluate(new IVariable[]{iApple, iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputStrengthVsBlockStrengthVsBlockSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeStrengthVsBlock() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_STRENGTH_VS_BLOCK.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- CAN_HARVEST_BLOCK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackCanHarvestBlock() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK.evaluate(new IVariable[]{iHoe, bStone});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"canharvestblock(hoe, stone) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK.evaluate(new IVariable[]{iPickaxe, bStone});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"canharvestblock(pickaxe, stone) = true\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK.evaluate(new IVariable[]{iPickaxe, bObsidian});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"canharvestblock(pickaxe, obsidian) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputCanHarvestBlockCanHarvestBlockLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK.evaluate(new IVariable[]{iApple, iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputCanHarvestBlockCanHarvestBlockSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeCanHarvestBlock() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_CAN_HARVEST_BLOCK.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- BLOCK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackBlock() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_BLOCK.evaluate(new IVariable[]{iStone});\n        Asserts.check(res1 instanceof ValueObjectTypeBlock.ValueBlock, \"result is a block\");\n        TestHelpers.assertEqual(((ValueObjectTypeBlock.ValueBlock) res1).getRawValue().get(), Blocks.STONE.defaultBlockState(), \"block(stone) = stone\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_BLOCK.evaluate(new IVariable[]{iDarkOakLeaves});\n        TestHelpers.assertEqual(((ValueObjectTypeBlock.ValueBlock) res2).getRawValue().get(), Blocks.DARK_OAK_LEAVES.defaultBlockState()\n                .setValue(LeavesBlock.DISTANCE, 7)\n                .setValue(LeavesBlock.PERSISTENT, false), \"block(dark_oak_leaves) = dark_oak_leaves\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputBlockBlockLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_BLOCK.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputBlockBlockSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_BLOCK.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeBlock() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_BLOCK.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISFLUIDSTACK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsFluidStack() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISFLUIDSTACK.evaluate(new IVariable[]{iHoe});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isfluidstack(hoe) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISFLUIDSTACK.evaluate(new IVariable[]{iBucketLava});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isfluidstack(bucketlava) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsFluidStackIsFluidStackLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISFLUIDSTACK.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsFluidStackIsFluidStackSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISFLUIDSTACK.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsFluidStack() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISFLUIDSTACK.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- FLUIDSTACK -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackFluidStack() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_FLUIDSTACK.evaluate(new IVariable[]{iHoe});\n        Asserts.check(res1 instanceof ValueObjectTypeFluidStack.ValueFluidStack, \"result is a fluidstack\");\n        TestHelpers.assertEqual(((ValueObjectTypeFluidStack.ValueFluidStack) res1).getRawValue().isEmpty(), true, \"fluidstack(hoe) = null\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_FLUIDSTACK.evaluate(new IVariable[]{iBucketLava});\n        TestHelpers.assertEqual(FluidStack.matches(((ValueObjectTypeFluidStack.ValueFluidStack) res2).getRawValue(), new FluidStack(Fluids.LAVA, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume())), true, \"fluidstack(bucketlava) = lava:1000\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_FLUIDSTACK.evaluate(new IVariable[]{iEmpty});\n        TestHelpers.assertEqual(FluidStack.matches(((ValueObjectTypeFluidStack.ValueFluidStack) res3).getRawValue(), FluidStack.EMPTY), true, \"fluidstack(empty) = empty\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputFluidStackFluidStackLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FLUIDSTACK.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputFluidStackFluidStackSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FLUIDSTACK.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeFluidStack() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FLUIDSTACK.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- FLUIDSTACK_CAPACITY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackFluidStackCapacity() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY.evaluate(new IVariable[]{iHoe});\n        Asserts.check(res1 instanceof ValueTypeLong.ValueLong, \"result is a fluidstack\");\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res1).getRawValue(), 0L, \"fluidstackcapacity(hoe) = 0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY.evaluate(new IVariable[]{iBucketLava});\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res2).getRawValue(), (long) IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume(), \"fluidstackcapacity(bucketlava) = 1000\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputFluidStackCapacityFluidStackCapacityLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputFluidStackCapacityFluidStackCapacitySmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeFluidStackCapacity() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FLUIDSTACKCAPACITY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISNBTEQUAL -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsNBTEqual() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISDATAEQUAL.evaluate(new IVariable[]{iHoe, iPickaxe});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isnbtequal(hoe, pickaxe) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISDATAEQUAL.evaluate(new IVariable[]{iHoe, iHoeEnchanted});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"isnbtequal(hoe, hoeenchanted) = false\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_ISDATAEQUAL.evaluate(new IVariable[]{iPickaxe, iPickaxe});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"isnbtequal(pickaxe, pickaxe) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsNBTEqualIsNBTEqualLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISDATAEQUAL.evaluate(new IVariable[]{iApple, iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsNBTEqualIsNBTEqualSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISDATAEQUAL.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsNBTEqual() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISDATAEQUAL.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISITEMEQUALNONBT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsItemEqualNoNBT() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iHoe, iPickaxe});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"israwitemequal(hoe, pickaxe) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iHoe, iHoeEnchanted});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"israwitemequal(hoe, hoeenchanted) = true\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iPickaxe, iPickaxe});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"israwitemequal(pickaxe, pickaxe) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsItemEqualNoNBTLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iApple, iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsItemEqualNoNBTSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsItemEqualNoNBT() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISRAWITEMEQUAL -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsRawItemEqual() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iHoe, iPickaxe});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"israwitemequal(hoe, pickaxe) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iHoe, iHoeEnchanted});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"israwitemequal(hoe, hoeenchanted) = true\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iPickaxe, iPickaxe});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"israwitemequal(pickaxe, pickaxe) = true\");\n\n        IValue res4 = Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iHoe, iHoe100});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), true, \"israwitemequal(hoe, hoe:100) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsRawItemEqualIsRawItemEqualLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iApple, iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputIsRawItemEqualIsRawItemEqualSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsRawItemEqual() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISRAWITEMEQUAL.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- MODNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackModName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_MODNAME.evaluate(new IVariable[]{iHoe});\n        Asserts.check(res1 instanceof ValueTypeString.ValueString, \"result is a string\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res1).getRawValue(), \"Minecraft\", \"modname(hoe) = Minecraft\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_MODNAME.evaluate(new IVariable[]{iWrench});\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) res2).getRawValue(), \"IntegratedDynamics\", \"modname(wrench) = IntegratedDynamics\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MODNAME.evaluate(new IVariable[]{iHoe, iHoe});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeModNameSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MODNAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeModName() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_MODNAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- FUELBURNTIME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackFuelBurnTime() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_FUELBURNTIME.evaluate(new IVariable[]{iBucketLava});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 20000, \"fuelburntime(bucketlava) = 20000\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_FUELBURNTIME.evaluate(new IVariable[]{iApple});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 0, \"fuelburntime(apple) = 0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputFuelBurnTimeFuelBurnTimeLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FUELBURNTIME.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputFuelBurnTimeFuelBurnTimeSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FUELBURNTIME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeFuelBurnTime() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_FUELBURNTIME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- CANBURN -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackCanBurn() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_CANBURN.evaluate(new IVariable[]{iBucketLava});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), true, \"canburn(bucketlava) = true\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_CANBURN.evaluate(new IVariable[]{iApple});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), false, \"canburn(apple) = false\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputCanBurnCanBurnLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_CANBURN.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputCanBurnCanBurnSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_CANBURN.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeCanBurn() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_CANBURN.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TAG -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackTag() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_TAG.evaluate(new IVariable[]{iStone});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 2, \"size(tag(stone)) = 2\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_TAG.evaluate(new IVariable[]{iWrench});\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res2).getRawValue().getLength(), 2, \"size(tag(wrench)) = 2\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TAG.evaluate(new IVariable[]{iHoe, iHoe});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TAG.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTag() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TAG.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TAG_STACKS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackTagStacks() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_TAG_STACKS.evaluate(new IVariable[]{sPlankWood});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), (int)Helpers.getTagValues(\"minecraft:planks\").count(), \"size(tag_stacks(plankWood))\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagStacksLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TAG_STACKS.evaluate(new IVariable[]{sPlankWood, sPlankWood});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTagStacksSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TAG_STACKS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTagStacks() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TAG_STACKS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITHSIZE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackWithSize() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_WITHSIZE.evaluate(new IVariable[]{iApple, int100});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an itemstack\");\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().getCount(), 100, \"withsize(apple, 100).stacksize == 100\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_WITHSIZE.evaluate(new IVariable[]{iBeef, int200});\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res2).getRawValue().getCount(), 200, \"withsize(beef, 200).stacksize == 200\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeWithSizeLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_WITHSIZE.evaluate(new IVariable[]{iApple, int100, int100});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeWithSizeSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_WITHSIZE.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeWithSize() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_WITHSIZE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ISFECONTAINER -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackIsFeContainer() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ISENERGYCONTAINER.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"isfecontainer(apple) == false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ISENERGYCONTAINER.evaluate(new IVariable[]{iEnergyBatteryEmpty});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"isfecontainer(energyBatteryEmpty) == true\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_ISENERGYCONTAINER.evaluate(new IVariable[]{iEnergyBatteryFull});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), true, \"isfecontainer(energyBatteryFull) == true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsFeContainerLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENERGYCONTAINER.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeIsFeContainerSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENERGYCONTAINER.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeIsFeContainer() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ISENERGYCONTAINER.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- STOREDFE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackStoredFe() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_STOREDENERGY.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeLong.ValueLong, \"result is a long\");\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res1).getRawValue(), 0L, \"storedfe(apple) == false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_STOREDENERGY.evaluate(new IVariable[]{iEnergyBatteryEmpty});\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res2).getRawValue(), 0L, \"storedfe(energyBatteryEmpty) == 0\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_STOREDENERGY.evaluate(new IVariable[]{iEnergyBatteryFull});\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res3).getRawValue(), (long) BlockEnergyBatteryConfig.capacity, \"storedfe(energyBatteryFull) == BlockEnergyBatteryConfig.capacity\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeStoredFeLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_STOREDENERGY.evaluate(new IVariable[]{iEnergyBatteryEmpty, iEnergyBatteryEmpty});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeStoredFeSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_STOREDENERGY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeStoredFe() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_STOREDENERGY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- FECAPACITY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackFeCapacity() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_ENERGYCAPACITY.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeLong.ValueLong, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res1).getRawValue(), 0L, \"fecapacity(apple) == false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_ENERGYCAPACITY.evaluate(new IVariable[]{iEnergyBatteryEmpty});\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res2).getRawValue(), (long) BlockEnergyBatteryConfig.capacity, \"fecapacity(energyBatteryEmpty) == BlockEnergyBatteryConfig.capacity\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_ENERGYCAPACITY.evaluate(new IVariable[]{iEnergyBatteryFull});\n        TestHelpers.assertEqual(((ValueTypeLong.ValueLong) res3).getRawValue(), (long) BlockEnergyBatteryConfig.capacity, \"fecapacity(energyBatteryFull) == BlockEnergyBatteryConfig.capacity\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeFeCapacityLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ENERGYCAPACITY.evaluate(new IVariable[]{iEnergyBatteryEmpty, iEnergyBatteryEmpty});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeFeCapacitySmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ENERGYCAPACITY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeFeCapacity() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_ENERGYCAPACITY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HASINVENTORY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackHasInventory() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_HASINVENTORY.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"hasinventory(apple) == false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_HASINVENTORY.evaluate(new IVariable[]{iShulkerBox});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"hasinventory(shulkerbox) == true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHasInventoryLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_HASINVENTORY.evaluate(new IVariable[]{iApple, int100});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeHasInventorySmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_HASINVENTORY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHasInventory() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_HASINVENTORY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- INVENTORYSIZE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackInventorySize() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_INVENTORYSIZE.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 0, \"inventorysize(apple) == 0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_INVENTORYSIZE.evaluate(new IVariable[]{iShulkerBox});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 27, \"inventory(shulkerbox) == 27\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeInventorySizeLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_INVENTORYSIZE.evaluate(new IVariable[]{iApple, int100});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeInventorySizeSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_INVENTORYSIZE.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeInventorySize() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_INVENTORYSIZE.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- INVENTORY -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackInventory() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_INVENTORY.evaluate(new IVariable[]{iApple});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 0, \"inventory(apple).size == 0\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_INVENTORY.evaluate(new IVariable[]{iShulkerBox});\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res2).getRawValue().getLength(), 27, \"inventory(shulkerbox).size == 27\");\n\n        IValue res3 = Operators.OBJECT_ITEMSTACK_INVENTORY.evaluate(new IVariable[]{iShulkerBox});\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) (((ValueTypeList.ValueList) res3).getRawValue().get(10))).getRawValue().getItem(), Items.APPLE, \"inventory(shulkerbox)[10] == apple\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputInventoryLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_INVENTORY.evaluate(new IVariable[]{iApple, int100});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputInventorySmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_INVENTORY.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeInventory() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_INVENTORY.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- ITEMBYNAME -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemItemByName() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_BY_NAME.evaluate(new IVariable[]{sApple});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is a block\");\n        TestHelpers.assertEqual(((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue().getItem(),\n                new ItemStack(Items.APPLE).getItem(), \"itembyname(minecraft:apple) = apple\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemByNameLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_BY_NAME.evaluate(new IVariable[]{sApple, sApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeItemByNameSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_BY_NAME.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeItemByName() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_BY_NAME.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- LIST_COUNT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackListCount() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_LIST_COUNT.evaluate(new IVariable[]{lApples, iApple});\n        Asserts.check(res1 instanceof ValueTypeInteger.ValueInteger, \"result is an integer\");\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res1).getRawValue(), 7, \"listcount(apple) = 7\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_LIST_COUNT.evaluate(new IVariable[]{lApples, iStone});\n        TestHelpers.assertEqual(((ValueTypeInteger.ValueInteger) res2).getRawValue(), 0, \"listcount(stone) = 0\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeListCountLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_LIST_COUNT.evaluate(new IVariable[]{lApples, iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeListCountSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_LIST_COUNT.evaluate(new IVariable[]{lApples});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeListCount() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_LIST_COUNT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DATA -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackNbt() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_DATA.evaluate(new IVariable[]{iAppleNoData});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an nbt tag\");\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue().isPresent(), false, \"data(apple:1) is null\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_DATA.evaluate(new IVariable[]{iEnergyBatteryFull});\n        TestHelpers.assertNonEqual(((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), new CompoundTag(), \"data(battery) is non null\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputNbtNbtLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputNbtNbtSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeNbt() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- HASDATA -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackHasNbt() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_HASDATA.evaluate(new IVariable[]{iEmpty});\n        Asserts.check(res1 instanceof ValueTypeBoolean.ValueBoolean, \"result is a boolean\");\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), false, \"hasdata(empty) = false\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_HASDATA.evaluate(new IVariable[]{iAppleTag});\n        TestHelpers.assertEqual(((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), true, \"hasdata(appleTag) = true\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputHasNbtHasNbtLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_HASDATA.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputHasNbtHasNbtSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_HASDATA.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeHasNbt() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_HASDATA.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DATA_KEYS -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackDataKeys() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_DATA_KEYS.evaluate(new IVariable[]{iEmpty});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList<?,?>, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<?,?>) res1).getRawValue().getLength(), 0, \"datakeys(empty) = []\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_DATA_KEYS.evaluate(new IVariable[]{iAppleTag});\n        TestHelpers.assertEqual(((ValueTypeList.ValueList<?,?>) res2).getRawValue().getLength(), 15, \"datakeys(appleTag).length = 15\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(0))).getRawValue(), \"minecraft:attribute_modifiers\", \"datakeys(appleTag)[0] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(1))).getRawValue(), \"minecraft:break_sound\", \"datakeys(appleTag)[1] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(2))).getRawValue(), \"minecraft:consumable\", \"datakeys(appleTag)[2] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(3))).getRawValue(), \"minecraft:enchantments\", \"datakeys(appleTag)[3] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(4))).getRawValue(), \"minecraft:food\", \"datakeys(appleTag)[4] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(5))).getRawValue(), \"minecraft:item_model\", \"datakeys(appleTag)[5] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(6))).getRawValue(), \"minecraft:item_name\", \"datakeys(appleTag)[6] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(7))).getRawValue(), \"minecraft:lore\", \"datakeys(appleTag)[7] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(8))).getRawValue(), \"minecraft:max_stack_size\", \"datakeys(appleTag)[8] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(9))).getRawValue(), \"minecraft:ominous_bottle_amplifier\", \"datakeys(appleTag)[9] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(10))).getRawValue(), \"minecraft:rarity\", \"datakeys(appleTag)[10] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(11))).getRawValue(), \"minecraft:repair_cost\", \"datakeys(appleTag)[11] == ...\");\n        TestHelpers.assertEqual(((ValueTypeString.ValueString) (((ValueTypeList.ValueList) res2).getRawValue().get(12))).getRawValue(), \"minecraft:swing_animation\", \"datakeys(appleTag)[12] == ...\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataKeysDataKeysLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA_KEYS.evaluate(new IVariable[]{iApple, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataKeysDataKeysSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA_KEYS.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDataKeys() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA_KEYS.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- DATA_VALUE -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackDataValue() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_DATA_VALUE.evaluate(new IVariable[]{iAppleNoData, sMaxStackSize});\n        Asserts.check(res1 instanceof ValueTypeNbt.ValueNbt, \"result is an empty tag\");\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res1).getRawValue().isEmpty(), true, \"datavalue(apple, maxstacksize) = empty\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_DATA_VALUE.evaluate(new IVariable[]{iAppleTag, sMaxStackSize});\n        TestHelpers.assertEqual(((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), IntTag.valueOf(64), \"datavalue(appleTag, maxstacksize).length = 64\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataValueDataValueLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA_VALUE.evaluate(new IVariable[]{iApple, sMaxStackSize, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputDataValueDataValueSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA_VALUE.evaluate(new IVariable[]{iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeDataValue() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_DATA_VALUE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_DATA -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackWithData() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_WITH_DATA.evaluate(new IVariable[]{iAppleNoData, sMaxStackSize, t4});\n        Asserts.check(res1 instanceof ValueObjectTypeItemStack.ValueItemStack, \"result is an item\");\n        ItemStack outItem1 = ((ValueObjectTypeItemStack.ValueItemStack) res1).getRawValue();\n        TestHelpers.assertEqual(outItem1.getItem(), iAppleNoData.getValue().getRawValue().getItem(), \"withdata(apple, maxstacksize, 4) = apple\");\n        TestHelpers.assertNonEqual(outItem1.getComponents(), iAppleNoData.getValue().getRawValue().getComponents(), \"withdata(apple, maxstacksize, 4) !=components apple\");\n        TestHelpers.assertEqual(outItem1.get(DataComponents.MAX_STACK_SIZE), 4, \"withdata(apple, maxstacksize, 4).maxstacksize = 4\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_WITH_DATA.evaluate(new IVariable[]{iAppleTag, sMaxStackSize, t4});\n        ItemStack outItem2 = ((ValueObjectTypeItemStack.ValueItemStack) res2).getRawValue();\n        TestHelpers.assertEqual(outItem2.getItem(), iAppleNoData.getValue().getRawValue().getItem(), \"withdata(appleTag, maxstacksize, 4) = apple\");\n        TestHelpers.assertNonEqual(outItem2.getComponents(), iAppleNoData.getValue().getRawValue().getComponents(), \"withdata(appleTag, maxstacksize, 4) !=components apple\");\n        TestHelpers.assertEqual(outItem2.get(DataComponents.MAX_STACK_SIZE), 4, \"withdata(appleTag, maxstacksize, 4).maxstacksize = 4\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputWithDataWithDataLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_WITH_DATA.evaluate(new IVariable[]{iApple, sMaxStackSize, t4, iApple});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputWithDataWithDataSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_WITH_DATA.evaluate(new IVariable[]{iApple, sMaxStackSize});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeWithData() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_WITH_DATA.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- TOOLTIPLINES -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testItemStackTooltip() throws EvaluationException {\n        IValue res1 = Operators.OBJECT_ITEMSTACK_TOOLTIP.evaluate(new IVariable[]{iPickaxe});\n        Asserts.check(res1 instanceof ValueTypeList.ValueList, \"result is a list\");\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res1).getRawValue().getLength(), 7, \"size(tooltip(pickaxe)) = 7\");\n\n        IValue res2 = Operators.OBJECT_ITEMSTACK_TOOLTIP.evaluate(new IVariable[]{iApple});\n        TestHelpers.assertEqual(((ValueTypeList.ValueList) res2).getRawValue().getLength(), 3, \"size(tooltip(apple)) = 3\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTooltipLarge() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TOOLTIP.evaluate(new IVariable[]{iPickaxe, iPickaxe});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputSizeTooltipSmall() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TOOLTIP.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInvalidInputTypeTooltip() throws EvaluationException {\n        Operators.OBJECT_ITEMSTACK_TOOLTIP.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestRecipeOperators.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.material.Fluids;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;\nimport org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;\nimport org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;\nimport org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.test.IntegrationBefore;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Test the different ingredients operators.\n * @author rubensworks\n */\npublic class TestRecipeOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableRecipe rMain;\n\n    private DummyVariableIngredients iMainOut;\n    private DummyVariableIngredients iItems;\n\n    @IntegrationBefore\n    public void before() {\n        List<List<IPrototypedIngredient<ItemStack, Integer>>> ingredientsIn = Lists.newArrayList();\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Items.OAK_BOAT), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Blocks.STONE), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n\n        Map<IngredientComponent<?, ?>, List<?>> ingredientsOut = Maps.newIdentityHashMap();\n        ingredientsOut.put(IngredientComponent.ENERGY, Lists.newArrayList(777L));\n        ingredientsOut.put(IngredientComponent.FLUIDSTACK, Lists.newArrayList(new FluidStack(Fluids.WATER, 123)));\n        ingredientsOut.put(IngredientComponent.ITEMSTACK, Lists.newArrayList(new ItemStack(Items.OAK_BOAT), new ItemStack(Item.byBlock(Blocks.STONE))));\n        iMainOut = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                new MixedIngredients(ingredientsOut)));\n\n        rMain = new DummyVariableRecipe(ValueObjectTypeRecipe.ValueRecipe.of(RecipeDefinition.ofIngredients(IngredientComponent.ITEMSTACK,\n                ingredientsIn,\n                iMainOut.getValue().getRawValue().get()\n        )));\n\n        iItems = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                MixedIngredients.ofInstances(IngredientComponent.ITEMSTACK, Lists.newArrayList(\n                        new ItemStack(Items.DIAMOND_PICKAXE), new ItemStack(Blocks.OAK_DOOR), ItemStack.EMPTY)\n                )));\n    }\n\n    /**\n     * ----------------------------------- INPUT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testInput() throws EvaluationException {\n        IValue res1 = Operators.RECIPE_INPUT.evaluate(new IVariable[]{rMain});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredients\");\n        TestHelpers.assertEqual(res1, ValueObjectTypeIngredients.ValueIngredients.of(MixedIngredients.fromRecipeInput(rMain.getValue().getRawValue().get())), \"input is correct\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInputSizeLarge() throws EvaluationException {\n        Operators.RECIPE_INPUT.evaluate(new IVariable[]{rMain, rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInputSizeSmall() throws EvaluationException {\n        Operators.RECIPE_INPUT.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testInputSize() throws EvaluationException {\n        Operators.RECIPE_INPUT.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- OUTPUT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testOutput() throws EvaluationException {\n        IValue res1 = Operators.RECIPE_OUTPUT.evaluate(new IVariable[]{rMain});\n        Asserts.check(res1 instanceof ValueObjectTypeIngredients.ValueIngredients, \"result is an ingredients\");\n        TestHelpers.assertEqual(res1, iMainOut.getValue(), \"output is correct\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testOutputSizeLarge() throws EvaluationException {\n        Operators.RECIPE_OUTPUT.evaluate(new IVariable[]{rMain, rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testOutputSizeSmall() throws EvaluationException {\n        Operators.RECIPE_OUTPUT.evaluate(new IVariable[]{});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testOutputSize() throws EvaluationException {\n        Operators.RECIPE_OUTPUT.evaluate(new IVariable[]{DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_INPUT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithInput() throws EvaluationException {\n        IValue res1 = Operators.RECIPE_WITH_INPUT.evaluate(new IVariable[]{rMain, iItems});\n        Asserts.check(res1 instanceof ValueObjectTypeRecipe.ValueRecipe, \"result is a recipe\");\n\n        List<List<IPrototypedIngredient<ItemStack, Integer>>> ingredientsIn = Lists.newArrayList();\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Items.DIAMOND_PICKAXE), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Blocks.OAK_DOOR), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n        IRecipeDefinition recipe = RecipeDefinition.ofIngredients(IngredientComponent.ITEMSTACK,\n                ingredientsIn,\n                iMainOut.getValue().getRawValue().get()\n        );\n        TestHelpers.assertEqual(res1, ValueObjectTypeRecipe.ValueRecipe.of(recipe), \"input is correct\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithInputSizeLarge() throws EvaluationException {\n        Operators.RECIPE_WITH_INPUT.evaluate(new IVariable[]{rMain, iItems, rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithInputSizeSmall() throws EvaluationException {\n        Operators.RECIPE_WITH_INPUT.evaluate(new IVariable[]{rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithInputSize() throws EvaluationException {\n        Operators.RECIPE_WITH_INPUT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_OUTPUT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithOutput() throws EvaluationException {\n        IValue res1 = Operators.RECIPE_WITH_OUTPUT.evaluate(new IVariable[]{rMain, iItems});\n        Asserts.check(res1 instanceof ValueObjectTypeRecipe.ValueRecipe, \"result is a recipe\");\n\n        List<List<IPrototypedIngredient<ItemStack, Integer>>> ingredientsIn = Lists.newArrayList();\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Items.OAK_BOAT), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Blocks.STONE), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n        IRecipeDefinition recipe = RecipeDefinition.ofIngredients(IngredientComponent.ITEMSTACK,\n                ingredientsIn,\n                iItems.getValue().getRawValue().get()\n        );\n        TestHelpers.assertEqual(res1, ValueObjectTypeRecipe.ValueRecipe.of(recipe), \"output is correct\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithOutputSizeLarge() throws EvaluationException {\n        Operators.RECIPE_WITH_OUTPUT.evaluate(new IVariable[]{rMain, iItems, rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithOutputSizeSmall() throws EvaluationException {\n        Operators.RECIPE_WITH_OUTPUT.evaluate(new IVariable[]{rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithOutputSize() throws EvaluationException {\n        Operators.RECIPE_WITH_OUTPUT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n\n    /**\n     * ----------------------------------- WITH_INPUT_OUTPUT -----------------------------------\n     */\n\n    @IntegrationTest\n    public void testWithInputOutput() throws EvaluationException {\n        IValue res1 = Operators.RECIPE_WITH_INPUT_OUTPUT.evaluate(new IVariable[]{iItems, iMainOut});\n        Asserts.check(res1 instanceof ValueObjectTypeRecipe.ValueRecipe, \"result is a recipe\");\n\n        List<List<IPrototypedIngredient<ItemStack, Integer>>> ingredientsIn = Lists.newArrayList();\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Items.DIAMOND_PICKAXE), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Blocks.OAK_DOOR), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n        IRecipeDefinition recipe = RecipeDefinition.ofIngredients(IngredientComponent.ITEMSTACK,\n                ingredientsIn,\n                iMainOut.getValue().getRawValue().get()\n        );\n        TestHelpers.assertEqual(res1, ValueObjectTypeRecipe.ValueRecipe.of(recipe), \"input is correct\");\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithInputOutputSizeLarge() throws EvaluationException {\n        Operators.RECIPE_WITH_INPUT_OUTPUT.evaluate(new IVariable[]{iItems, iMainOut, rMain});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithInputOutputSizeSmall() throws EvaluationException {\n        Operators.RECIPE_WITH_INPUT_OUTPUT.evaluate(new IVariable[]{iItems});\n    }\n\n    @IntegrationTest(expected = EvaluationException.class)\n    public void testWithInputOutputSize() throws EvaluationException {\n        Operators.RECIPE_WITH_INPUT_OUTPUT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE});\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/TestVariables.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.gson.JsonParseException;\nimport net.minecraft.nbt.*;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.material.Fluids;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;\nimport org.cyclops.commoncapabilities.api.ingredient.*;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;\nimport org.cyclops.integrateddynamics.core.test.IntegrationTest;\nimport org.cyclops.integrateddynamics.core.test.TestHelpers;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.cyclops.integrateddynamics.core.test.TestHelpers.deserialize;\nimport static org.cyclops.integrateddynamics.core.test.TestHelpers.serialize;\n\n/**\n * Test the different variable types.\n * @author rubensworks\n */\npublic class TestVariables {\n\n    protected static Tag serializeStack(ItemStack itemStack) {\n        return ItemStack.OPTIONAL_CODEC.encodeStart(NbtOps.INSTANCE, itemStack)\n                .getOrThrow(JsonParseException::new);\n    }\n\n    protected static Tag serializeFluidStack(FluidStack fluidStack) {\n        return FluidStack.OPTIONAL_CODEC.encodeStart(NbtOps.INSTANCE, fluidStack)\n                .getOrThrow(JsonParseException::new);\n    }\n\n    @IntegrationTest\n    public void testIngredientsType() {\n        DummyVariableIngredients inull = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(null));\n        TestHelpers.assertEqual(inull.getValue().getRawValue().orElse(null), null, \"null value is null\");\n\n        IMixedIngredients ingredients1 =\n                MixedIngredients.ofInstances(IngredientComponent.ITEMSTACK, Lists.newArrayList(\n                        ItemStack.EMPTY, new ItemStack(Items.OAK_BOAT), new ItemStack(Blocks.STONE), ItemStack.EMPTY));\n        DummyVariableIngredients i0 = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients\n                .of(ingredients1));\n        TestHelpers.assertEqual(i0.getValue().getRawValue().get(), ingredients1, \"ingredient value is ingredient\");\n\n        CompoundTag tag = new CompoundTag();\n        ListTag itemStacks = new ListTag();\n        CompoundTag itemStack1 = new CompoundTag();\n        itemStack1.put(\"i\", serializeStack(ItemStack.EMPTY));\n        itemStacks.add(itemStack1);\n        CompoundTag itemStack2 = new CompoundTag();\n        itemStack2.put(\"i\", serializeStack(new ItemStack(Items.OAK_BOAT)));\n        itemStacks.add(itemStack2);\n        CompoundTag itemStack3 = new CompoundTag();\n        itemStack3.put(\"i\", serializeStack(new ItemStack(Blocks.STONE)));\n        itemStacks.add(itemStack3);\n        CompoundTag itemStack4 = new CompoundTag();\n        itemStack4.put(\"i\", serializeStack(ItemStack.EMPTY));\n        itemStacks.add(itemStack4);\n        ListTag ingredientsList = new ListTag();\n        CompoundTag ingredientsListItemStacks = new CompoundTag();\n        ingredientsListItemStacks.putString(\"component\", \"minecraft:itemstack\");\n        ingredientsListItemStacks.put(\"instances\", itemStacks);\n        ingredientsList.add(ingredientsListItemStacks);\n        tag.put(\"v\", ingredientsList);\n\n        CompoundTag tagRoot = new CompoundTag();\n        tagRoot.put(\"v\", tag);\n\n        TestHelpers.assertEqual(serialize(o -> i0.getType().serialize(o, i0.getValue())), tagRoot, \"Serialization is correct\");\n        TestHelpers.assertEqual(deserialize(tagRoot, i0.getType()::deserialize), i0.getValue(), \"Deserialization is correct\");\n    }\n\n    @IntegrationTest\n    public void testRecipeType() {\n        DummyVariableRecipe rnull = new DummyVariableRecipe(ValueObjectTypeRecipe.ValueRecipe.of(null));\n        TestHelpers.assertEqual(rnull.getValue().getRawValue().orElse(null), null, \"null value is null\");\n\n        List<List<IPrototypedIngredient<ItemStack, Integer>>> ingredientsIn = Lists.newArrayList();\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Items.OAK_BOAT), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, new ItemStack(Blocks.STONE), ItemMatch.EXACT)));\n        ingredientsIn.add(Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, ItemStack.EMPTY, ItemMatch.EXACT)));\n\n        Map<IngredientComponent<?, ?>, List<?>> ingredientsOut = Maps.newIdentityHashMap();\n        ingredientsOut.put(IngredientComponent.ENERGY, Lists.newArrayList(777L));\n        ingredientsOut.put(IngredientComponent.FLUIDSTACK, Lists.newArrayList(new FluidStack(Fluids.WATER, 123)));\n        ingredientsOut.put(IngredientComponent.ITEMSTACK, Lists.newArrayList(new ItemStack(Items.OAK_BOAT), new ItemStack(Item.byBlock(Blocks.STONE))));\n        DummyVariableIngredients iMainOut = new DummyVariableIngredients(ValueObjectTypeIngredients.ValueIngredients.of(\n                new MixedIngredients(ingredientsOut)));\n        IRecipeDefinition rawRecipe = RecipeDefinition.ofIngredients(\n                IngredientComponent.ITEMSTACK,\n                ingredientsIn,\n                iMainOut.getValue().getRawValue().get()\n        );\n        DummyVariableRecipe r0 = new DummyVariableRecipe(ValueObjectTypeRecipe.ValueRecipe.of(rawRecipe));\n        TestHelpers.assertEqual(r0.getValue().getRawValue().get(), rawRecipe, \"recipe value is recipe\");\n\n        CompoundTag tag = new CompoundTag();\n\n        CompoundTag output = new CompoundTag();\n        ListTag outputList = new ListTag();\n        output.put(\"v\", outputList);\n        ListTag energies = new ListTag();\n        CompoundTag energy = new CompoundTag();\n        energy.putLong(\"i\", 777L);\n        energies.add(energy);\n        CompoundTag outputListEnergy = new CompoundTag();\n        outputListEnergy.putString(\"component\", \"minecraft:energy\");\n        outputListEnergy.put(\"instances\", energies);\n        outputList.add(outputListEnergy);\n        ListTag fluidStacks = new ListTag();\n        CompoundTag fluidStack1 = new CompoundTag();\n        fluidStack1.put(\"i\", serializeFluidStack(new FluidStack(Fluids.WATER, 123)));\n        fluidStacks.add(fluidStack1);\n        CompoundTag outputListFluidStack = new CompoundTag();\n        outputListFluidStack.putString(\"component\", \"minecraft:fluidstack\");\n        outputListFluidStack.put(\"instances\", fluidStacks);\n        outputList.add(outputListFluidStack);\n        ListTag itemStacks = new ListTag();\n        CompoundTag itemStack1 = new CompoundTag();\n        itemStack1.put(\"i\", serializeStack(new ItemStack(Items.OAK_BOAT)));\n        itemStacks.add(itemStack1);\n        CompoundTag itemStack2 = new CompoundTag();\n        itemStack2.put(\"i\", serializeStack(new ItemStack(Blocks.STONE)));\n        itemStacks.add(itemStack2);\n        CompoundTag outputListItemStack = new CompoundTag();\n        outputListItemStack.putString(\"component\", \"minecraft:itemstack\");\n        outputListItemStack.put(\"instances\", itemStacks);\n        outputList.add(outputListItemStack);\n\n        ListTag input = new ListTag();\n        ListTag itemStacksIn = new ListTag();\n        itemStacksIn.add(new CompoundTag());\n        itemStacksIn.add(new CompoundTag());\n        itemStacksIn.add(new CompoundTag());\n        itemStacksIn.add(new CompoundTag());\n\n        ListTag val0 = new ListTag();\n        val0.add(serialize(o -> IPrototypedIngredient.serialize(o, ingredientsIn.get(0).get(0))));\n        val0.getCompound(0).get().remove(\"ingredientComponent\");\n        CompoundTag val0l = new CompoundTag();\n        val0l.put(\"l\", val0);\n        itemStacksIn.getCompound(0).get().put(\"val\", val0l);\n        itemStacksIn.getCompound(0).get().putByte(\"type\", (byte) 0);\n\n        ListTag val1 = new ListTag();\n        val1.add(serialize(o -> IPrototypedIngredient.serialize(o, ingredientsIn.get(1).get(0))));\n        val1.getCompound(0).get().remove(\"ingredientComponent\");\n        CompoundTag val1l = new CompoundTag();\n        val1l.put(\"l\", val1);\n        itemStacksIn.getCompound(1).get().put(\"val\", val1l);\n        itemStacksIn.getCompound(1).get().putByte(\"type\", (byte) 0);\n\n        ListTag val2 = new ListTag();\n        val2.add(serialize(o -> IPrototypedIngredient.serialize(o, ingredientsIn.get(2).get(0))));\n        val2.getCompound(0).get().remove(\"ingredientComponent\");\n        CompoundTag val2l = new CompoundTag();\n        val2l.put(\"l\", val2);\n        itemStacksIn.getCompound(2).get().put(\"val\", val2l);\n        itemStacksIn.getCompound(2).get().putByte(\"type\", (byte) 0);\n\n        ListTag val3 = new ListTag();\n        val3.add(serialize(o -> IPrototypedIngredient.serialize(o, ingredientsIn.get(3).get(0))));\n        val3.getCompound(0).get().remove(\"ingredientComponent\");\n        CompoundTag val3l = new CompoundTag();\n        val3l.put(\"l\", val3);\n        itemStacksIn.getCompound(3).get().put(\"val\", val3l);\n        itemStacksIn.getCompound(3).get().putByte(\"type\", (byte) 0);\n\n        CompoundTag ingredientsListItemStacks = new CompoundTag();\n        ingredientsListItemStacks.putString(\"component\", \"minecraft:itemstack\");\n        ingredientsListItemStacks.put(\"instances\", itemStacksIn);\n        input.add(ingredientsListItemStacks);\n\n        int[] reusableBytes = new int[]{0, 0, 0, 0};\n        IntArrayTag itemStacksReusable = new IntArrayTag(reusableBytes);\n        ingredientsListItemStacks.put(\"reusable\", itemStacksReusable);\n\n        tag.put(\"output\", output);\n        tag.put(\"input\", input);\n\n        CompoundTag topTag = new CompoundTag();\n        topTag.put(\"v\", tag);\n\n        TestHelpers.assertEqual(serialize(o -> r0.getType().serialize(o, r0.getValue())), topTag, \"Serialization is correct\");\n        TestHelpers.assertEqual(deserialize(topTag, r0.getType()::deserialize), r0.getValue(), \"Deserialization is correct\");\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/gametest/integration/ValueEntityMock.java",
    "content": "package org.cyclops.integrateddynamics.gametest.integration;\n\nimport net.minecraft.world.entity.Entity;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeEntity;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * @author rubensworks\n */\npublic class ValueEntityMock extends ValueObjectTypeEntity.ValueEntity {\n\n    private final Entity entity;\n\n    public ValueEntityMock(@Nullable Entity entity) {\n        super(entity);\n        this.entity = entity;\n    }\n\n    @Override\n    public Optional<Entity> getRawValue() {\n        return Optional.of(entity);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/OnTheDynamicsOfIntegrationBook.java",
    "content": "package org.cyclops.integrateddynamics.infobook;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.cyclopscore.infobook.pageelement.SectionAppendix;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.infobook.pageelement.*;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.w3c.dom.Element;\n\nimport java.util.List;\n\n/**\n * Infobook class for the On the Dynamics of Integration.\n * @author rubensworks\n */\npublic class OnTheDynamicsOfIntegrationBook extends InfoBook {\n\n    private static OnTheDynamicsOfIntegrationBook _instance = null;\n\n    static {\n        InfoBookParser.registerAppendixRecipeFactories(RegistryEntries.RECIPETYPE_SQUEEZER.get(), SqueezerRecipeAppendix::new);\n        InfoBookParser.registerAppendixRecipeFactories(RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER.get(), MechanicalSqueezerRecipeAppendix::new);\n        InfoBookParser.registerAppendixRecipeFactories(RegistryEntries.RECIPETYPE_DRYING_BASIN.get(), DryingBasinRecipeAppendix::new);\n        InfoBookParser.registerAppendixRecipeFactories(RegistryEntries.RECIPETYPE_MECHANICAL_DRYING_BASIN.get(), MechanicalDryingBasinRecipeAppendix::new);\n\n        InfoBookParser.registerAppendixFactory(Reference.MOD_ID + \":aspect\", new InfoBookParser.IAppendixFactory() {\n            @Override\n            public SectionAppendix create(IInfoBook infoBook, Element node) throws InfoBookParser.InvalidAppendixException {\n                String aspectName = node.getTextContent();\n                IAspect aspect = Aspects.REGISTRY.getAspect(Identifier.parse(aspectName));\n                if (aspect == null) {\n                    throw new InfoBookParser.InvalidAppendixException(String.format(\"Could not find an aspect by name %s.\", aspectName));\n                }\n                return new AspectAppendix(infoBook, aspect);\n            }\n        });\n\n        InfoBookParser.registerAppendixListFactory(Reference.MOD_ID + \":part_aspects\", new InfoBookParser.IAppendixListFactory() {\n            @Override\n            public List<SectionAppendix> create(final IInfoBook infoBook, Element node) throws InfoBookParser.InvalidAppendixException {\n                String partName = node.getTextContent();\n                IPartType partType = PartTypes.REGISTRY.getPartType(Identifier.parse(partName));\n                if (partType == null) {\n                    throw new InfoBookParser.InvalidAppendixException(String.format(\"Could not find a part type by name '%s'.\", partName));\n                }\n                List<IAspect> aspects = Lists.newArrayList(Aspects.REGISTRY.getAspects(partType));\n                List<SectionAppendix> appendices = Lists.newArrayList();\n                for (IAspect aspect : aspects) {\n                    appendices.add(new AspectAppendix(infoBook, aspect));\n                }\n                return appendices;\n            }\n        });\n\n        InfoBookParser.registerAppendixFactory(Reference.MOD_ID + \":operator\", new InfoBookParser.IAppendixFactory() {\n            @Override\n            public SectionAppendix create(IInfoBook infoBook, Element node) throws InfoBookParser.InvalidAppendixException {\n                String operatorName = node.getTextContent();\n                IOperator operator = Operators.REGISTRY.getOperator(Identifier.parse(operatorName));\n                if (operator == null) {\n                    throw new InfoBookParser.InvalidAppendixException(String.format(\"Could not find an operator by name %s.\", operatorName));\n                }\n                return new OperatorAppendix(infoBook, operator);\n            }\n        });\n\n        InfoBookParser.registerAppendixListFactory(Reference.MOD_ID + \":operators_output\", new InfoBookParser.IAppendixListFactory() {\n            @Override\n            public List<SectionAppendix> create(final IInfoBook infoBook, Element node) throws InfoBookParser.InvalidAppendixException {\n                String categoryName = node.getTextContent();\n                List<IOperator> operators = Lists.newArrayList(\"*\".equals(categoryName) ? Operators.REGISTRY.getOperators() : Operators.REGISTRY.getOperatorsInCategory(categoryName));\n                if (operators.isEmpty()) {\n                    operators = Lists.newArrayList();\n                    for (IOperator operator : Operators.REGISTRY.getOperators()) {\n                        if (operator.getUniqueName().toString().matches(categoryName)) {\n                            operators.add(operator);\n                        }\n                    }\n                }\n                List<SectionAppendix> appendices = Lists.newArrayList();\n                for (IOperator operator : operators) {\n                    appendices.add(new OperatorAppendix(infoBook, operator));\n                }\n                return appendices;\n            }\n        });\n    }\n\n    private OnTheDynamicsOfIntegrationBook() {\n        super(IntegratedDynamics._instance, 2, Reference.BOOK_URL);\n    }\n\n    public static synchronized OnTheDynamicsOfIntegrationBook getInstance() {\n        if(_instance == null) {\n            _instance = new OnTheDynamicsOfIntegrationBook();\n        }\n        return _instance;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/AspectAppendix.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.cyclopscore.infobook.InfoSection;\nimport org.cyclops.cyclopscore.infobook.pageelement.SectionAppendix;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.item.AspectVariableFacade;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * Aspect appendix.\n * @author rubensworks\n */\npublic class AspectAppendix extends SectionAppendix<AspectAppendixClient> {\n\n    private final IAspect<?, ?> aspect;\n    private ItemStack itemStack;\n\n    public AspectAppendix(IInfoBook infoBook, IAspect<?, ?> aspect) throws InfoBookParser.InvalidAppendixException {\n        super(infoBook);\n        this.aspect = aspect;\n    }\n\n    public IAspect<?, ?> getAspect() {\n        return aspect;\n    }\n\n    public ItemStack getItemStack() {\n        return itemStack;\n    }\n\n    @Override\n    protected int getOffsetY() {\n        return 5;\n    }\n\n    @Override\n    protected int getWidth() {\n        return 100;\n    }\n\n    @Override\n    protected int getHeight() {\n        return 30 + (aspect.hasProperties() ? 10 * (aspect.getPropertyTypes().size() + 1) : 0);\n    }\n\n    @Override\n    public AspectAppendixClient constructSectionAppendixClient() {\n        return new AspectAppendixClient(this);\n    }\n\n    @Override\n    public void preBakeElement(InfoSection infoSection) {\n\n    }\n\n    @Override\n    public void bakeElement(InfoSection infoSection) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        itemStack = registry.writeVariableFacadeItem(new ItemStack(RegistryEntries.ITEM_VARIABLE),\n                new AspectVariableFacade(false, 0, aspect), Aspects.REGISTRY);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/AspectAppendixClient.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.infobook.ScreenInfoBook;\nimport org.cyclops.cyclopscore.infobook.pageelement.SectionAppendixClient;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic class AspectAppendixClient extends SectionAppendixClient<AspectAppendix> {\n\n    private static final int SLOT_SIZE = 16;\n\n    protected AspectAppendixClient(AspectAppendix sectionAppendix) {\n        super(sectionAppendix);\n    }\n\n    @Override\n    protected void drawElement(ScreenInfoBook gui, GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int page, int mx, int my) {\n        AspectAppendix section = getSectionAppendix();\n        IAspect aspect = section.getAspect();\n\n        int yOffset = 5;\n        gui.drawOuterBorder(guiGraphics, x - 1, y - 1 - yOffset, section.getWidth() + 2, section.getHeight() + 2, 0.5F, 0.5F, 0.5F, 0.4f);\n        gui.drawTextBanner(guiGraphics, x + width / 2, y - 2 - yOffset);\n        gui.drawScaledCenteredString(guiGraphics, IModHelpers.get().getL10NHelpers().localize(\"aspect.integrateddynamics.name\"), x, y - 2 - yOffset, width, 0.9f, gui.getBannerWidth() - 6, IModHelpers.get().getBaseHelpers().RGBAToInt(30, 20, 120, 255));\n\n        guiGraphics.item(section.getItemStack(), x, y);\n\n        // Base information\n        String aspectName = IModHelpers.get().getL10NHelpers().localize(aspect.getTranslationKey());\n        String valueTypeName = IModHelpers.get().getL10NHelpers().localize(aspect.getValueType().getTranslationKey());\n        gui.drawScaledCenteredString(guiGraphics, IModHelpers.get().getL10NHelpers().localize(aspectName), x + 10, y + 8, width, 1f, gui.getBannerWidth() - 10, ARGB.opaque(0));\n        String valueString = IModHelpers.get().getL10NHelpers().localize(aspect.getValueType().getDisplayColorFormat() + valueTypeName);\n        //gui.getFont().setBidiFlag(true);\n        guiGraphics.text(gui.getFont(), IModHelpers.get().getL10NHelpers().localize(aspect instanceof IAspectWrite ? L10NValues.GUI_INPUT : L10NValues.GUI_OUTPUT, valueString), x, y + 16, ARGB.opaque(0), false);\n\n        // Settings\n        if (aspect.hasProperties()) {\n            int offsetY = 26;\n            guiGraphics.text(gui.getFont(), ChatFormatting.DARK_GRAY + IModHelpers.get().getL10NHelpers().localize(\"gui.integrateddynamics.part.properties\"), x, y + offsetY, ARGB.opaque(0), false);\n            for (IAspectPropertyTypeInstance property : ((IAspect<?, ?>) aspect).getPropertyTypes()) {\n                offsetY += 10;\n                guiGraphics.text(gui.getFont(), ChatFormatting.DARK_GRAY + IModHelpers.get().getL10NHelpers().localize(property.getTranslationKey()), x + 10, y + offsetY, ARGB.opaque(0), false);\n            }\n        }\n        //gui.getFont().setBidiFlag(wasUnicode);\n    }\n\n    @Override\n    protected void postDrawElement(ScreenInfoBook gui, GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int page, int mx, int my) {\n        if(mx >= x && my >= y && mx <= x + SLOT_SIZE && my <= y + SLOT_SIZE ) {\n            List<Component> lines = Lists.newArrayList();\n            getSectionAppendix().getAspect().loadTooltip(lines::add, true);\n            guiGraphics.setComponentTooltipForNextFrame(gui.getFont(), lines, mx, my);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/DryingBasinRecipeAppendix.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.display.RecipeDisplayEntry;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.infobook.AdvancedButtonEnum;\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.cyclopscore.infobook.InfoSection;\nimport org.cyclops.cyclopscore.infobook.pageelement.RecipeAppendix;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\nimport java.util.function.Supplier;\n\n/**\n * Drying basin recipes.\n * @author rubensworks\n */\npublic class DryingBasinRecipeAppendix extends RecipeAppendix<DryingBasinRecipeAppendixClient> {\n\n    public static final int SLOT_INPUT_OFFSET_X = 16;\n    public static final int SLOT_OFFSET_Y = 23;\n    public static final int SLOT_OUTPUT_OFFSET_X = 68;\n\n    public static final AdvancedButtonEnum INPUT_ITEM = AdvancedButtonEnum.create();\n    public static final AdvancedButtonEnum INPUT_FLUID = AdvancedButtonEnum.create();\n    public static final AdvancedButtonEnum RESULT_ITEM = AdvancedButtonEnum.create();\n    public static final AdvancedButtonEnum RESULT_FLUID = AdvancedButtonEnum.create();\n\n    public DryingBasinRecipeAppendix(IInfoBook infoBook, Supplier<RecipeDisplayEntry> recipeDisplaySupplier) throws InfoBookParser.InvalidAppendixException {\n        super(infoBook, recipeDisplaySupplier);\n    }\n\n    @Override\n    protected int getWidth() {\n        return SLOT_OUTPUT_OFFSET_X + 32;\n    }\n\n    @Override\n    public DryingBasinRecipeAppendixClient constructSectionAppendixClient() throws InfoBookParser.InvalidAppendixException {\n        return new DryingBasinRecipeAppendixClient(this);\n    }\n\n    @Override\n    protected int getHeightInner() {\n        return 42;\n    }\n\n    @Override\n    protected String getUnlocalizedTitle() {\n        return \"block.integrateddynamics.drying_basin\";\n    }\n\n    @Override\n    public void bakeElement(InfoSection infoSection) {\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            getSectionAppendixClient().bakeElement(infoSection);\n        }\n        super.bakeElement(infoSection);\n    }\n\n    protected ItemStack getCrafter() {\n        return new ItemStack(RegistryEntries.BLOCK_DRYING_BASIN.get());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/DryingBasinRecipeAppendixClient.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.util.context.ContextMap;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.display.RecipeDisplayEntry;\nimport net.minecraft.world.item.crafting.display.SlotDisplayContext;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.infobook.*;\nimport org.cyclops.cyclopscore.infobook.pageelement.RecipeAppendixClient;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplayDryingBasin;\n\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class DryingBasinRecipeAppendixClient extends RecipeAppendixClient<DryingBasinRecipeAppendix> {\n\n    protected static final int SLOT_SIZE = 16;\n\n    protected DryingBasinRecipeAppendixClient(DryingBasinRecipeAppendix sectionAppendix) {\n        super(sectionAppendix);\n    }\n\n    public void bakeElement(InfoSection infoSection) {\n        Map<AdvancedButtonEnum, AdvancedButton> renderItemHolders = getSectionAppendix().getRenderItemHolders();\n        IInfoBook infoBook = getSectionAppendix().getInfoBook();\n        renderItemHolders.put(DryingBasinRecipeAppendix.INPUT_ITEM, new ItemButton(infoBook));\n        renderItemHolders.put(DryingBasinRecipeAppendix.INPUT_FLUID, new FluidButton(infoBook));\n        renderItemHolders.put(DryingBasinRecipeAppendix.RESULT_ITEM, new ItemButton(infoBook));\n        renderItemHolders.put(DryingBasinRecipeAppendix.RESULT_FLUID, new FluidButton(infoBook));\n    }\n\n    @Override\n    public void drawElementInner(ScreenInfoBook gui, GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int page, int mx, int my) {\n        int middle = (width - SLOT_SIZE) / 2;\n        gui.drawArrowRight(guiGraphics, x + middle - 3, y + 2);\n\n        // Prepare items\n        RecipeDisplayEntry recipeDisplay = getSectionAppendix().getRecipeDisplay();\n        if (recipeDisplay == null) {\n            return;\n        }\n        int tick = getTick(gui);\n        ContextMap contextMap = SlotDisplayContext.fromLevel(Minecraft.getInstance().level);\n        RecipeDisplayDryingBasin display = ((RecipeDisplayDryingBasin) recipeDisplay.display());\n        ItemStack inputItem = prepareItemStacks(display.inputIngredient().resolveForStacks(contextMap), tick);\n        FluidStack inputFluid = display.inputFluid();\n        ItemStack resultItem = prepareItemStacks(display.outputItem().resolveForStacks(contextMap), tick);\n        FluidStack resultFluid = display.outputFluid();\n\n        // Items\n        renderItem(gui, guiGraphics, x + DryingBasinRecipeAppendix.SLOT_INPUT_OFFSET_X, y, inputItem, mx, my, DryingBasinRecipeAppendix.INPUT_ITEM);\n        renderFluid(gui, guiGraphics, x + DryingBasinRecipeAppendix.SLOT_INPUT_OFFSET_X, y + DryingBasinRecipeAppendix.SLOT_OFFSET_Y, inputFluid, mx, my, DryingBasinRecipeAppendix.INPUT_FLUID);\n        renderItem(gui, guiGraphics, x + DryingBasinRecipeAppendix.SLOT_OUTPUT_OFFSET_X, y, resultItem, mx, my, DryingBasinRecipeAppendix.RESULT_ITEM);\n        renderFluid(gui, guiGraphics, x + DryingBasinRecipeAppendix.SLOT_OUTPUT_OFFSET_X, y + DryingBasinRecipeAppendix.SLOT_OFFSET_Y, resultFluid, mx, my, DryingBasinRecipeAppendix.RESULT_FLUID);\n\n        renderItem(gui, guiGraphics, x + middle, y, getSectionAppendix().getCrafter(), mx, my, false, null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/MechanicalDryingBasinRecipeAppendix.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.display.RecipeDisplayEntry;\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\nimport java.util.function.Supplier;\n\n/**\n * Mechanical drying basin recipes.\n * @author rubensworks\n */\npublic class MechanicalDryingBasinRecipeAppendix extends DryingBasinRecipeAppendix {\n    public MechanicalDryingBasinRecipeAppendix(IInfoBook infoBook, Supplier<RecipeDisplayEntry> recipeDisplaySupplier) throws InfoBookParser.InvalidAppendixException {\n        super(infoBook, recipeDisplaySupplier);\n    }\n\n    @Override\n    protected String getUnlocalizedTitle() {\n        return \"block.integrateddynamics.mechanical_drying_basin\";\n    }\n\n    protected ItemStack getCrafter() {\n        return new ItemStack(RegistryEntries.BLOCK_MECHANICAL_DRYING_BASIN.get());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/MechanicalSqueezerRecipeAppendix.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.display.RecipeDisplayEntry;\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\nimport java.util.function.Supplier;\n\n/**\n * Mechanical squeezer recipes.\n * @author rubensworks\n */\npublic class MechanicalSqueezerRecipeAppendix extends SqueezerRecipeAppendix {\n    public MechanicalSqueezerRecipeAppendix(IInfoBook infoBook, Supplier<RecipeDisplayEntry> recipeDisplaySupplier) throws InfoBookParser.InvalidAppendixException {\n        super(infoBook, recipeDisplaySupplier);\n    }\n\n    @Override\n    protected String getUnlocalizedTitle() {\n        return \"block.integrateddynamics.mechanical_squeezer\";\n    }\n\n    @Override\n    protected ItemStack getCrafter() {\n        return new ItemStack(RegistryEntries.BLOCK_MECHANICAL_SQUEEZER.get());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/OperatorAppendix.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.cyclopscore.infobook.InfoSection;\nimport org.cyclops.cyclopscore.infobook.pageelement.SectionAppendix;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\n\n/**\n * Operator appendix.\n * @author rubensworks\n */\npublic class OperatorAppendix extends SectionAppendix<OperatorAppendixClient> {\n\n    private final IOperator operator;\n\n    public OperatorAppendix(IInfoBook infoBook, IOperator operator) throws InfoBookParser.InvalidAppendixException {\n        super(infoBook);\n        this.operator = operator;\n    }\n\n    public IOperator getOperator() {\n        return operator;\n    }\n\n    @Override\n    protected int getOffsetY() {\n        return 5;\n    }\n\n    @Override\n    protected int getWidth() {\n        return 100;\n    }\n\n    @Override\n    protected int getHeight() {\n        return 46 + (operator.getInputTypes().length) * 8;\n    }\n\n    @Override\n    public OperatorAppendixClient constructSectionAppendixClient() {\n        return new OperatorAppendixClient(this);\n    }\n\n    @Override\n    public void preBakeElement(InfoSection infoSection) {\n\n    }\n\n    @Override\n    public void bakeElement(InfoSection infoSection) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/OperatorAppendixClient.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ARGB;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.infobook.ScreenInfoBook;\nimport org.cyclops.cyclopscore.infobook.pageelement.SectionAppendixClient;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.List;\n\n/**\n * @author rubensworks\n */\npublic class OperatorAppendixClient extends SectionAppendixClient<OperatorAppendix> {\n    protected OperatorAppendixClient(OperatorAppendix sectionAppendix) {\n        super(sectionAppendix);\n    }\n\n    @Override\n    protected void drawElement(ScreenInfoBook gui, GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int page, int mx, int my) {\n        OperatorAppendix section = getSectionAppendix();\n        IOperator operator = section.getOperator();\n\n        int yOffset = 5;\n        gui.drawOuterBorder(guiGraphics, x - 1, y - 1 - yOffset, section.getWidth() + 2, section.getHeight() + 2, 0.5F, 0.5F, 0.5F, 0.4f);\n        gui.drawTextBanner(guiGraphics, x + width / 2, y - 2 - yOffset);\n        gui.drawScaledCenteredString(guiGraphics, IModHelpers.get().getL10NHelpers().localize(\"operator.integrateddynamics\"), x, y - 2 - yOffset, width, 0.9f, gui.getBannerWidth() - 6, IModHelpers.get().getBaseHelpers().RGBAToInt(30, 20, 120, 255));\n\n        // Base information\n        String operatorName = IModHelpers.get().getL10NHelpers().localize(operator.getTranslationKey());\n        gui.drawScaledCenteredString(guiGraphics, IModHelpers.get().getL10NHelpers().localize(operatorName) + \" (\" + operator.getSymbol() + \")\", x, y + 8, width, 1f, gui.getBannerWidth(), ARGB.opaque(0));\n\n        // Input/output types\n        IValueType[] inputTypes = operator.getInputTypes();\n        int offsetY = 14;\n        for(int i = 0; i < inputTypes.length; i++) {\n            guiGraphics.text(gui.getFont(), IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_INPUT, (i + 1) + \": \"\n                    + inputTypes[i].getDisplayColorFormat() + IModHelpers.get().getL10NHelpers().localize(inputTypes[i].getTranslationKey())), x, y + offsetY, ARGB.opaque(0), false);\n            offsetY += 8;\n        }\n        String outputTypeName = IModHelpers.get().getL10NHelpers().localize(operator.getOutputType().getTranslationKey());\n        guiGraphics.text(gui.getFont(), IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_OUTPUT,\n                operator.getOutputType().getDisplayColorFormat() + outputTypeName), x, y + offsetY, ARGB.opaque(0), false);\n\n        // Global/local names\n        offsetY += 8;\n        gui.drawScaledCenteredString(guiGraphics, IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_OPERATOR_GLOBALNAME,\n                operator.getGlobalInteractName()), x, y + offsetY + 6, width, 1f, gui.getBannerWidth(), ARGB.opaque(0));\n        offsetY += 8;\n        if (operator.getInputTypes().length > 0) {\n            String scopedTypeName = IModHelpers.get().getL10NHelpers().localize(operator.getInputTypes()[0].getTranslationKey());\n            gui.drawScaledCenteredString(guiGraphics, IModHelpers.get().getL10NHelpers().localize(L10NValues.GUI_OPERATOR_LOCALNAME,\n                    operator.getInputTypes()[0].getDisplayColorFormat() + scopedTypeName + \".\" + operator.getScopedInteractName()), x, y + offsetY + 6, width, 1f, gui.getBannerWidth(), ARGB.opaque(0));\n        }\n    }\n\n    @Override\n    protected void postDrawElement(ScreenInfoBook gui, GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int page, int mx, int my) {\n        if(mx >= x && my >= y && mx <= x + getSectionAppendix().getWidth() && my <= y + gui.getFont().lineHeight ) {\n            List<Component> lines = Lists.newArrayList();\n            getSectionAppendix().getOperator().loadTooltip(lines::add, true);\n            guiGraphics.setComponentTooltipForNextFrame(gui.getFont(), lines, mx, my);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/SqueezerRecipeAppendix.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.display.RecipeDisplayEntry;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.infobook.AdvancedButtonEnum;\nimport org.cyclops.cyclopscore.infobook.IInfoBook;\nimport org.cyclops.cyclopscore.infobook.InfoBookParser;\nimport org.cyclops.cyclopscore.infobook.InfoSection;\nimport org.cyclops.cyclopscore.infobook.pageelement.RecipeAppendix;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplaySqueezer;\n\nimport java.util.List;\nimport java.util.function.Supplier;\n\n/**\n * Squeezer recipes.\n * @author rubensworks\n */\npublic class SqueezerRecipeAppendix extends RecipeAppendix<SqueezerRecipeAppendixClient> {\n\n    public static final int SLOT_INPUT_OFFSET_X = 16;\n    public static final int SLOT_OFFSET_Y = 23;\n    public static final int SLOT_OUTPUT_OFFSET_X = 68;\n\n    public static final AdvancedButtonEnum INPUT_ITEM = AdvancedButtonEnum.create();\n    public static final AdvancedButtonEnum RESULT_FLUID = AdvancedButtonEnum.create();\n\n    private List<AdvancedButtonEnum> resultItems;\n\n    public SqueezerRecipeAppendix(IInfoBook infoBook, Supplier<RecipeDisplayEntry> recipeDisplaySupplier) throws InfoBookParser.InvalidAppendixException {\n        super(infoBook, recipeDisplaySupplier);\n    }\n\n    public List<AdvancedButtonEnum> getResultItems() {\n        return resultItems;\n    }\n\n    @Override\n    protected int getWidth() {\n        return SLOT_OUTPUT_OFFSET_X + 32;\n    }\n\n    @Override\n    public SqueezerRecipeAppendixClient constructSectionAppendixClient() throws InfoBookParser.InvalidAppendixException {\n        return new SqueezerRecipeAppendixClient(this);\n    }\n\n    @Override\n    protected int getHeightInner() {\n        RecipeDisplayEntry recipeDisplay = getRecipeDisplay();\n        if (recipeDisplay == null) {\n            return 10;\n        }\n        return (!((RecipeDisplaySqueezer) recipeDisplay.display()).outputFluid().isEmpty() ? SLOT_OFFSET_Y : 0) + ((RecipeDisplaySqueezer) recipeDisplay.display()).outputItems().size() * SLOT_OFFSET_Y - 3;\n    }\n\n    @Override\n    protected String getUnlocalizedTitle() {\n        return \"block.integrateddynamics.squeezer\";\n    }\n\n    @Override\n    public void bakeElement(InfoSection infoSection) {\n        resultItems = Lists.newArrayList();\n\n        RecipeDisplayEntry recipeDisplay = getRecipeDisplay();\n        if (recipeDisplay == null) {\n            return;\n        }\n\n        RecipeDisplaySqueezer display = ((RecipeDisplaySqueezer) recipeDisplay.display());\n        for (int i = 0; i < display.outputItems().size(); i++) {\n            resultItems.add(AdvancedButtonEnum.create());\n        }\n\n        if (IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            getSectionAppendixClient().bakeElement(infoSection);\n        }\n        super.bakeElement(infoSection);\n    }\n\n    protected ItemStack getCrafter()  {\n        return new ItemStack(RegistryEntries.BLOCK_SQUEEZER.get());\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/infobook/pageelement/SqueezerRecipeAppendixClient.java",
    "content": "package org.cyclops.integrateddynamics.infobook.pageelement;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.GuiGraphicsExtractor;\nimport net.minecraft.util.context.ContextMap;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.display.RecipeDisplayEntry;\nimport net.minecraft.world.item.crafting.display.SlotDisplayContext;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.infobook.*;\nimport org.cyclops.cyclopscore.infobook.pageelement.RecipeAppendixClient;\nimport org.cyclops.integrateddynamics.core.recipe.display.RecipeDisplaySqueezer;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author rubensworks\n */\npublic class SqueezerRecipeAppendixClient extends RecipeAppendixClient<SqueezerRecipeAppendix> {\n\n    protected static final int SLOT_SIZE = 16;\n\n    public SqueezerRecipeAppendixClient(SqueezerRecipeAppendix appendix) {\n        super(appendix);\n    }\n\n    public void bakeElement(InfoSection infoSection) {\n        Map<AdvancedButtonEnum, AdvancedButton> renderItemHolders = getSectionAppendix().getRenderItemHolders();\n        IInfoBook infoBook = getSectionAppendix().getInfoBook();\n        renderItemHolders.put(SqueezerRecipeAppendix.INPUT_ITEM, new ItemButton(infoBook));\n        for (AdvancedButtonEnum resultItem : getSectionAppendix().getResultItems()) {\n            renderItemHolders.put(resultItem, new ItemButton(infoBook));\n        }\n        renderItemHolders.put(SqueezerRecipeAppendix.RESULT_FLUID, new FluidButton(infoBook));\n    }\n\n    @Override\n    public void drawElementInner(ScreenInfoBook gui, GuiGraphicsExtractor guiGraphics, int x, int y, int width, int height, int page, int mx, int my) {\n        int middle = (width - SLOT_SIZE) / 2;\n        gui.drawArrowRight(guiGraphics, x + middle - 3, y + 2);\n\n        // Prepare items\n        RecipeDisplayEntry recipeDisplay = this.getSectionAppendix().getRecipeDisplay();\n        if (recipeDisplay == null) {\n            return;\n        }\n        int tick = getTick(gui);\n        ContextMap contextMap = SlotDisplayContext.fromLevel(Minecraft.getInstance().level);\n        RecipeDisplaySqueezer display = ((RecipeDisplaySqueezer) recipeDisplay.display());\n        ItemStack inputItem = prepareItemStacks(display.inputIngredient().resolveForStacks(contextMap), tick);\n        List<ItemStack> outputItems = display.outputItems().stream()\n                .map(pair -> pair.getLeft().resolveForFirstStack(contextMap))\n                .toList();\n        FluidStack outputFluid = display.outputFluid();\n\n        // Items\n        renderItem(gui, guiGraphics, x + SqueezerRecipeAppendix.SLOT_INPUT_OFFSET_X, y, inputItem, mx, my, SqueezerRecipeAppendix.INPUT_ITEM);\n        int slotOffset = 0;\n        for (int i = 0; i < outputItems.size(); i++) {\n            renderItem(gui, guiGraphics, x + SqueezerRecipeAppendix.SLOT_OUTPUT_OFFSET_X, y + slotOffset, outputItems.get(i), mx, my, this.getSectionAppendix().getResultItems().get(i),\n                    display.outputItems().get(i).getRight());\n            slotOffset += SqueezerRecipeAppendix.SLOT_OFFSET_Y;\n        }\n        if (!outputFluid.isEmpty()) {\n            renderFluid(gui, guiGraphics, x + SqueezerRecipeAppendix.SLOT_OUTPUT_OFFSET_X, y + slotOffset, outputFluid, mx, my, SqueezerRecipeAppendix.RESULT_FLUID);\n        }\n\n        renderItem(gui, guiGraphics, x + middle, y, this.getSectionAppendix().getCrafter(), mx, my, false, null);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerCoalGenerator.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.DataSlot;\nimport net.minecraft.world.inventory.Slot;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityCoalGenerator;\nimport org.cyclops.integrateddynamics.inventory.slot.FurnaceFuelSlotExtended;\n\n/**\n * Container for the coal generator.\n * @author rubensworks\n */\npublic class ContainerCoalGenerator extends InventoryContainer {\n\n    private final DataSlot referenceProgress;\n\n    public ContainerCoalGenerator(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityCoalGenerator.INVENTORY_SIZE), DataSlot.standalone());\n    }\n\n    public ContainerCoalGenerator(int id, Inventory playerInventory, Container inventory, DataSlot progressReference) {\n        super(RegistryEntries.CONTAINER_COAL_GENERATOR.get(), id, playerInventory, inventory);\n\n        this.referenceProgress = addDataSlot(progressReference);\n\n        addInventory(inventory, 0, offsetX + 80, offsetY + 11, 1, 1);\n        addPlayerInventory(playerInventory, offsetX + 8, offsetY + 46);\n    }\n\n    @Override\n    public Slot createNewSlot(Container inventory, int index, int row, int column) {\n        if(inventory instanceof Inventory) {\n            return super.createNewSlot(inventory, index, row, column);\n        }\n        return new FurnaceFuelSlotExtended(inventory, index, row, column, () -> this.player.level().fuelValues());\n    }\n\n    public int getProgress() {\n        return this.referenceProgress.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerCoalGeneratorConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerCoalGenerator}.\n * @author rubensworks\n */\npublic class ContainerCoalGeneratorConfig extends GuiConfigCommon<ContainerCoalGenerator, IModBase> {\n\n    public ContainerCoalGeneratorConfig() {\n        super(IntegratedDynamics._instance,\n                \"coal_generator\",\n                eConfig -> new MenuType<>(ContainerCoalGenerator::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerCoalGenerator> getScreenFactoryProvider() {\n        return new ContainerCoalGeneratorConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerCoalGeneratorConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenCoalGenerator;\n\n/**\n * @author rubensworks\n */\npublic class ContainerCoalGeneratorConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerCoalGenerator> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerCoalGenerator>> MenuScreens.ScreenConstructor<ContainerCoalGenerator, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenCoalGenerator::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerDelay.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.slot.SlotRemoveOnly;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityDelay;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\n\nimport java.util.Optional;\n\n/**\n * Container for the delay.\n * @author rubensworks\n */\npublic class ContainerDelay extends ContainerActiveVariableBase<BlockEntityDelay> {\n\n    private final int lastUpdateValueId;\n    private final int lastCapacityValueId;\n\n    public ContainerDelay(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityDelay.INVENTORY_SIZE), Optional.empty());\n    }\n\n    public ContainerDelay(int id, Inventory playerInventory, Container inventory,\n                          Optional<BlockEntityDelay> tileSupplier) {\n        super(RegistryEntries.CONTAINER_DELAY.get(), id, playerInventory, inventory, tileSupplier);\n        addSlot(new SlotVariable(inventory, BlockEntityProxy.SLOT_READ, 81, 25));\n        addSlot(new SlotVariable(inventory, BlockEntityProxy.SLOT_WRITE_IN, 56, 78));\n        addSlot(new SlotRemoveOnly(inventory, BlockEntityProxy.SLOT_WRITE_OUT, 104, 78));\n        addPlayerInventory(playerInventory, offsetX + 9, offsetY + 145);\n\n        lastUpdateValueId = getNextValueId();\n        lastCapacityValueId = getNextValueId();\n        getTileSupplier().ifPresent(tile -> tile.setLastPlayer(playerInventory.player));\n    }\n\n    @Override\n    protected void initializeValues() {\n        ValueNotifierHelpers.setValue(this, lastUpdateValueId, getTileSupplier().map(BlockEntityDelay::getUpdateInterval).orElse(0));\n        ValueNotifierHelpers.setValue(this, lastCapacityValueId, getTileSupplier().map(BlockEntityDelay::getCapacity).orElse(0));\n    }\n\n    public int getLastUpdateValueId() {\n        return lastUpdateValueId;\n    }\n\n    public int getLastCapacityValueId() {\n        return lastCapacityValueId;\n    }\n\n    public int getLastUpdateValue() {\n        return ValueNotifierHelpers.getValueInt(this, getLastUpdateValueId());\n    }\n\n    public int getLastCapacityValue() {\n        return ValueNotifierHelpers.getValueInt(this, getLastCapacityValueId());\n    }\n\n    @Override\n    public void onUpdate(int valueId, CompoundTag value) {\n        super.onUpdate(valueId, value);\n        getTileSupplier().ifPresent(tile -> {\n            if (valueId == getLastUpdateValueId()) {\n                tile.setUpdateInterval(getLastUpdateValue());\n            } else if (valueId == getLastCapacityValueId()) {\n                tile.setCapacity(getLastCapacityValue());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerDelayConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerDelay}.\n * @author rubensworks\n */\npublic class ContainerDelayConfig extends GuiConfigCommon<ContainerDelay, IModBase> {\n\n    public ContainerDelayConfig() {\n        super(IntegratedDynamics._instance,\n                \"delay\",\n                eConfig -> new MenuType<>(ContainerDelay::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerDelay> getScreenFactoryProvider() {\n        return new ContainerDelayConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerDelayConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenDelay;\n\n/**\n * @author rubensworks\n */\npublic class ContainerDelayConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerDelay> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerDelay>> MenuScreens.ScreenConstructor<ContainerDelay, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenDelay::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLabeller.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.StringUtils;\nimport org.cyclops.cyclopscore.inventory.ItemLocation;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.container.ItemInventoryContainer;\nimport org.cyclops.cyclopscore.inventory.slot.SlotExtended;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.item.ItemLabeller;\n\n/**\n * Container for the labeller.\n * @author rubensworks\n */\npublic class ContainerLabeller extends ItemInventoryContainer<ItemLabeller> {\n\n    private SimpleInventory temporaryInputSlots = null;\n\n    public ContainerLabeller(int id, Inventory inventory, FriendlyByteBuf packetBuffer) {\n        this(id, inventory, ItemLocation.readFromPacketBuffer(packetBuffer));\n    }\n\n    public ContainerLabeller(int id, Inventory inventory, ItemLocation itemLocation) {\n        super(RegistryEntries.CONTAINER_LABELLER.get(), id, inventory, itemLocation);\n        this.temporaryInputSlots = new SimpleInventory(1, 1);\n        addSlot(new SlotExtended(temporaryInputSlots, 0, 8, 8));\n        this.addPlayerInventory(player.getInventory(), 8, 31);\n    }\n\n    public SimpleInventory getTemporaryInputSlots() {\n        return temporaryInputSlots;\n    }\n\n    public ItemStack getItemStack() {\n        return temporaryInputSlots.getItem(0);\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return 1;\n    }\n\n    @Override\n    public void removed(Player player) {\n        super.removed(player);\n        if (!player.level().isClientSide()) {\n            ItemStack itemStack = temporaryInputSlots.getItem(0);\n            if(!itemStack.isEmpty()) {\n                Helpers.returnItemToPlayer(player, itemStack);\n            }\n        }\n    }\n\n    public void setItemStackName(String name) {\n        ItemStack itemStack = getItemStack();\n        if(!itemStack.isEmpty()) {\n            if (StringUtils.isBlank(name)) {\n                itemStack.remove(DataComponents.CUSTOM_NAME);\n            } else {\n                itemStack.set(DataComponents.CUSTOM_NAME, Component.literal(name));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLabellerConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerLabeller}.\n * @author rubensworks\n */\npublic class ContainerLabellerConfig extends GuiConfigCommon<ContainerLabeller, IModBase> {\n\n    public ContainerLabellerConfig() {\n        super(IntegratedDynamics._instance,\n                \"labeller\",\n                eConfig -> new ContainerTypeData<>(ContainerLabeller::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerLabeller> getScreenFactoryProvider() {\n        return new ContainerLabellerConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLabellerConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLabeller;\n\n/**\n * @author rubensworks\n */\npublic class ContainerLabellerConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerLabeller> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerLabeller>> MenuScreens.ScreenConstructor<ContainerLabeller, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenLabeller::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammer.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Container for the {@link org.cyclops.integrateddynamics.block.BlockLogicProgrammer}.\n * @author rubensworks\n */\npublic class ContainerLogicProgrammer extends ContainerLogicProgrammerBase {\n\n    public ContainerLogicProgrammer(int id, Inventory playerInventory) {\n        super(RegistryEntries.CONTAINER_LOGIC_PROGRAMMER.get(), id, playerInventory);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerBase.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.util.StringUtil;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.ContainerInput;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.container.ScrollingInventoryContainer;\nimport org.cyclops.cyclopscore.inventory.slot.SlotSingleItem;\nimport org.cyclops.cyclopscore.persist.IDirtyMarkListener;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * Base container for the logic programmer.\n * @author rubensworks\n */\npublic abstract class ContainerLogicProgrammerBase extends ScrollingInventoryContainer<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> implements IDirtyMarkListener {\n\n    public static final int OUTPUT_X = 232;\n    public static final int OUTPUT_Y = 110;\n\n    public static final int BASE_X = 88;\n    public static final int BASE_Y = 18;\n    public static final int MAX_WIDTH = 160;\n    public static final int MAX_HEIGHT = 87;\n\n    protected static final IItemPredicate<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> FILTERER =\n            (item, pattern) -> pattern.matcher(item.getMatchString()).matches()\n                    || pattern.matcher(item.getSymbol()).matches();\n\n    private final SimpleInventory writeSlot;\n    private final SimpleInventory filterSlots;\n    private ILogicProgrammerElement activeElement = null;\n    private ILogicProgrammerElement temporarySlotsElement = null;\n    private SimpleInventory temporaryInputSlots = null;\n    private Component lastError;\n    private LoadConfigListener loadConfigListener;\n\n    private IValueType filterIn1 = null;\n    private IValueType filterIn2 = null;\n    private IValueType filterOut = null;\n\n    private ScreenCallbackHandler screenCallbackHandler;\n\n    private String lastLabel = \"\";\n\n    public ContainerLogicProgrammerBase(@Nullable MenuType<?> type, int id, Inventory playerInventory) {\n        super(type, id, playerInventory, new SimpleContainer(0), getElements(), FILTERER);\n        this.writeSlot = new SimpleInventory(1, 1);\n        this.filterSlots = new SimpleInventory(3, 1);\n        this.filterSlots.addDirtyMarkListener(new FilterSlotListener(ValueDeseralizationContext.of(playerInventory.player.level())));\n        this.writeSlot.addDirtyMarkListener(this);\n        this.writeSlot.addDirtyMarkListener(loadConfigListener = new LoadConfigListener());\n        this.temporaryInputSlots = new SimpleInventory(0, 1);\n        initializeSlotsPre();\n        initializeSlotsPost();\n    }\n\n    protected static List<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> getElements() {\n        List<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> elements = Lists.newLinkedList();\n        for(ILogicProgrammerElementType type: LogicProgrammerElementTypes.REGISTRY.getTypes()) {\n            elements.addAll(type.createElements());\n        }\n        return elements;\n    }\n\n    public void setScreenCallbackHandler(ScreenCallbackHandler screenCallbackHandler) {\n        this.screenCallbackHandler = screenCallbackHandler;\n    }\n\n    public ScreenCallbackHandler getGui() {\n        return this.screenCallbackHandler;\n    }\n\n    protected void initializeSlotsPre() {\n        addSlot(new SlotVariable(writeSlot, 0, OUTPUT_X, OUTPUT_Y));\n        SlotSingleItem filterSlotIn1 = new SlotVariable(filterSlots, 0, 6, 218);\n        SlotSingleItem filterSlotIn2 = new SlotVariable(filterSlots, 1, 24, 218);\n        SlotSingleItem filterSlotOut = new SlotVariable(filterSlots, 2, 58, 218);\n        filterSlotIn1.setPhantom(true);\n        filterSlotIn2.setPhantom(true);\n        filterSlotOut.setPhantom(true);\n        addSlot(filterSlotIn1);\n        addSlot(filterSlotIn2);\n        addSlot(filterSlotOut);\n    }\n\n    protected void initializeSlotsPost() {\n        addPlayerInventory((Inventory) getPlayerIInventory(), 88, 131);\n    }\n\n    @Override\n    public int getPageSize() {\n        return 10;\n    }\n\n    @Override\n    protected boolean isAssertInventorySize() {\n        return false;\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return 1;\n    }\n\n    public void setActiveElementById(Identifier typeId, Identifier elementId) {\n        ILogicProgrammerElementType type = LogicProgrammerElementTypes.REGISTRY.getType(typeId);\n        if (type != null) {\n            ILogicProgrammerElement element = type.getByName(elementId);\n            if(!LogicProgrammerElementTypes.areEqual(getActiveElement(), element)) {\n                setActiveElement(element, 0, 0);\n                onDirty();\n            }\n        } else {\n            setActiveElement(null, 0, 0);\n        }\n    }\n\n    /**\n     * Set the new active element.\n     * @param activeElement The new element.\n     * @param baseX The slots X coordinate\n     * @param baseY The slots Y coordinate\n     */\n    public void setActiveElement(final ILogicProgrammerElement activeElement, int baseX, int baseY) {\n        if(this.activeElement != null) {\n            this.activeElement.deactivate();\n        }\n        this.activeElement = activeElement;\n\n        this.lastError = null;\n        this.activeElement = activeElement;\n\n        this.setElementInventory(this.activeElement, baseX, baseY);\n\n        if(activeElement != null) {\n            activeElement.activate();\n        }\n    }\n\n    /**\n     * Set the new active element.\n     * @param element The new element.\n     * @param baseX The slots X coordinate\n     * @param baseY The slots Y coordinate\n     */\n    public void setElementInventory(final ILogicProgrammerElement element, int baseX, int baseY) {\n        this.lastError = null;\n\n        // This assumes that there is only one other slot, the remaining slots will be erased!\n        // (We can do this because they are all ghost slots)\n        lastSlots.clear();\n        slots.clear();\n        remoteSlots.clear();\n        initializeSlotsPre();\n        this.temporaryInputSlots.removeDirtyMarkListener(this);\n        this.temporaryInputSlots = new SimpleInventory(element == null ? 0 : element.getRenderPattern().getSlotPositions().length, element == null ? 0 : element.getItemStackSizeLimit());\n        temporaryInputSlots.addDirtyMarkListener(this);\n        this.temporarySlotsElement = element;\n        if(element != null) {\n            Pair<Integer, Integer>[] slotPositions = element.getRenderPattern().getSlotPositions();\n            for (int i = 0; i < temporaryInputSlots.getContainerSize(); i++) {\n                Slot slot = addSlot(element.createSlot(temporaryInputSlots, i, 1 + baseX + slotPositions[i].getLeft(),\n                        1 + baseY + slotPositions[i].getRight()));\n\n                // Init remote slot as empty, as the server may otherwise unnecessarily overwrite the client-side-only value.\n                remoteSlots.get(slot.index).force(ItemStack.EMPTY);\n            }\n        }\n        initializeSlotsPost();\n        this.lastLabel = \"\";\n    }\n\n    public boolean canWriteActiveElementPre() {\n        if(activeElement != null) {\n            return activeElement.canWriteElementPre();\n        }\n        return false;\n    }\n\n    public boolean canWriteActiveElement() {\n        if(!canWriteActiveElementPre()) {\n            return false;\n        }\n        lastError = activeElement.validate();\n        return lastError == null;\n    }\n\n    public ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?> getActiveElement() {\n        return activeElement;\n    }\n\n    @Override\n    public void removed(Player player) {\n        super.removed(player);\n        if (!player.level().isClientSide()) {\n            returnWriteItemToPlayer();\n        }\n    }\n\n    public void onLabelPacket(String label) {\n        this.lastLabel = label;\n        labelCurrent();\n    }\n\n    protected void labelCurrent() {\n        ItemStack itemStack = writeSlot.getItem(0);\n        if(!itemStack.isEmpty()) {\n            IVariableFacade variableFacade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(ValueDeseralizationContext.of(player.level()), itemStack);\n            if(this.lastLabel != null && variableFacade.isValid()) {\n                LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().put(variableFacade.getId(), this.lastLabel);\n            }\n        }\n    }\n\n    protected ItemStack writeElementInfo() {\n        ItemStack itemStack = writeSlot.getItem(0);\n        ItemStack result = getActiveElement().writeElement(player, itemStack.copy());\n        return result;\n    }\n\n    @Override\n    public void onDirty() {\n        ILogicProgrammerElement activeElement = getActiveElement();\n        if(activeElement != null) {\n            for (int i = 0; i < temporaryInputSlots.getContainerSize(); i++) {\n                ItemStack itemStack = temporaryInputSlots.getItem(i);\n                temporarySlotsElement.onInputSlotUpdated(player, i, itemStack);\n            }\n        }\n\n        ItemStack itemStack = writeSlot.getItem(0);\n        if(canWriteActiveElement() && !itemStack.isEmpty()) {\n            // If the variable has a vanilla custom name, make sure we inherit it as variable label\n            if (itemStack.has(DataComponents.CUSTOM_NAME)) {\n                this.lastLabel = itemStack.getHoverName().getString();\n            }\n\n            ItemStack outputStack = writeElementInfo();\n            writeSlot.removeDirtyMarkListener(this);\n            writeSlot.removeDirtyMarkListener(loadConfigListener);\n            writeSlot.setItem(0, outputStack);\n            if(!StringUtil.isNullOrEmpty(this.lastLabel)) {\n                labelCurrent();\n            }\n            writeSlot.addDirtyMarkListener(this);\n            writeSlot.addDirtyMarkListener(loadConfigListener);\n        }\n    }\n\n    protected void loadConfigFrom(ItemStack itemStack) {\n        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n        IVariableFacade variableFacade = registry.handle(ValueDeseralizationContext.of(player.level()), itemStack);\n        for(ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?> element : getUnfilteredItems()) {\n            if(element.isFor(variableFacade)) {\n                writeSlot.removeDirtyMarkListener(this);\n                writeSlot.removeDirtyMarkListener(loadConfigListener);\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                    getGui().handleElementActivation(element, false);\n                } else {\n                    setActiveElement(element, 0, 0);\n                }\n                temporaryInputSlots.removeDirtyMarkListener(this);\n                element.loadElement(variableFacade);\n                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {\n                    element.getClient().setValueInGui(getGui().getOperatorConfigPattern());\n                } else {\n                    element.setValueInContainer(this);\n                }\n                writeSlot.addDirtyMarkListener(this);\n                writeSlot.addDirtyMarkListener(loadConfigListener);\n                temporaryInputSlots.addDirtyMarkListener(this);\n            }\n        }\n    }\n\n    public Component getLastError() {\n        return this.lastError;\n    }\n\n    public SimpleInventory getTemporaryInputSlots() {\n        return this.temporaryInputSlots;\n    }\n\n    public boolean hasWriteItemInSlot() {\n        return !this.writeSlot.getItem(0).isEmpty();\n    }\n\n    public void returnWriteItemToPlayer() {\n        if (hasWriteItemInSlot()) {\n            ItemStack itemStack = writeSlot.getItem(0);\n            Helpers.returnItemToPlayer(player, itemStack);\n            writeSlot.setItem(0, ItemStack.EMPTY);\n        }\n    }\n\n    @Override\n    protected boolean additionalApplies(ILogicProgrammerElement item) {\n        return (\n                ((filterIn1 == null || item.matchesInput(filterIn1)) && (filterIn2 == null || item.matchesInput(filterIn2))) || (filterIn1 == null && filterIn2 == null))\n                && (filterOut == null || item.matchesOutput(filterOut));\n    }\n\n    @Override\n    public void clicked(int slotId, int mouseButton, ContainerInput clickType, Player player) {\n        // Handle cases where the client may have more (phantom) slots than the server.\n        if (slotId >= this.slots.size() || (this.activeElement != null\n                && this.slots.size() > slotId && slotId >= 0\n                && this.activeElement.slotClick(slotId, this.getSlot(slotId), mouseButton, clickType, player))) {\n            return;\n        }\n        super.clicked(slotId, mouseButton, clickType, player);\n    }\n\n    /**\n     * Load existing operator data when a variable card is inserted into the write slot\n     */\n    protected class LoadConfigListener implements IDirtyMarkListener {\n\n        @Override\n        public void onDirty() {\n            if (activeElement == null) {\n                ItemStack itemStack = writeSlot.getItem(0);\n                if (!itemStack.isEmpty()) {\n                    ContainerLogicProgrammerBase.this.loadConfigFrom(itemStack);\n                }\n            }\n        }\n\n    }\n\n    /**\n     * Filter LP elements based on the filter value types.\n     */\n    protected class FilterSlotListener implements IDirtyMarkListener {\n\n        private final ValueDeseralizationContext valueDeseralizationContext;\n\n        public FilterSlotListener(ValueDeseralizationContext valueDeseralizationContext) {\n            this.valueDeseralizationContext = valueDeseralizationContext;\n        }\n\n        protected IValueType getValueType(Container inventory, int slot) {\n            IVariableFacadeHandlerRegistry handler = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);\n            if(inventory.getItem(slot) != null) {\n                IVariableFacade variableFacade = handler.handle(valueDeseralizationContext, inventory.getItem(slot));\n                if(variableFacade.isValid()) {\n                    return variableFacade.getOutputType();\n                }\n            }\n            return null;\n        }\n\n        @Override\n        public void onDirty() {\n            IValueType filterIn1Prev = filterIn1;\n            IValueType filterIn2Prev = filterIn2;\n            IValueType filterOutPrev = filterOut;\n\n            filterIn1 = getValueType(filterSlots, 0);\n            filterIn2 = getValueType(filterSlots, 1);\n            filterOut = getValueType(filterSlots, 2);\n            if (filterIn1Prev != filterIn1 || filterIn2Prev != filterIn2 || filterOutPrev != filterOut) {\n                refreshFilter();\n            }\n        }\n\n    }\n\n    public static interface ScreenCallbackHandler {\n        public boolean handleElementActivation(ILogicProgrammerElement element, boolean sendToServer);\n        public RenderPattern getOperatorConfigPattern();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerLogicProgrammer}.\n * @author rubensworks\n */\npublic class ContainerLogicProgrammerConfig extends GuiConfigCommon<ContainerLogicProgrammer, IModBase> {\n\n    public ContainerLogicProgrammerConfig() {\n        super(IntegratedDynamics._instance,\n                \"logic_programmer\",\n                eConfig -> new MenuType<>(ContainerLogicProgrammer::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerLogicProgrammer> getScreenFactoryProvider() {\n        return new ContainerLogicProgrammerConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammer;\n\n/**\n * @author rubensworks\n */\npublic class ContainerLogicProgrammerConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerLogicProgrammer> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerLogicProgrammer>> MenuScreens.ScreenConstructor<ContainerLogicProgrammer, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenLogicProgrammer::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerPortable.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.inventory.ItemLocation;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.item.ItemPortableLogicProgrammer;\n\n/**\n * Container for the {@link ItemPortableLogicProgrammer}.\n * @author rubensworks\n */\npublic class ContainerLogicProgrammerPortable extends ContainerLogicProgrammerBase {\n\n    private final ItemLocation itemLocation;\n\n    public ContainerLogicProgrammerPortable(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, ItemLocation.readFromPacketBuffer(packetBuffer));\n    }\n\n    public ContainerLogicProgrammerPortable(int id, Inventory playerInventory, ItemLocation itemLocation) {\n        super(RegistryEntries.CONTAINER_LOGIC_PROGRAMMER_PORTABLE.get(), id, playerInventory);\n        this.itemLocation = itemLocation;\n    }\n\n    public ItemStack getItemStack(Player player) {\n        return this.itemLocation.getItemStack(player);\n    }\n\n    @Override\n    public boolean stillValid(Player playerIn) {\n        ItemStack item = getItemStack(player);\n        return item != null && item.getItem() == RegistryEntries.ITEM_PORTABLE_LOGIC_PROGRAMMER.get();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerPortableConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerLogicProgrammerPortable}.\n * @author rubensworks\n */\npublic class ContainerLogicProgrammerPortableConfig extends GuiConfigCommon<ContainerLogicProgrammerPortable, IModBase> {\n\n    public ContainerLogicProgrammerPortableConfig() {\n        super(IntegratedDynamics._instance,\n                \"logic_programmer_portable\",\n                eConfig -> new ContainerTypeData<>(ContainerLogicProgrammerPortable::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerLogicProgrammerPortable> getScreenFactoryProvider() {\n        return new ContainerLogicProgrammerPortableConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerPortableConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerPortable;\n\n/**\n * @author rubensworks\n */\npublic class ContainerLogicProgrammerPortableConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerLogicProgrammerPortable> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerLogicProgrammerPortable>> MenuScreens.ScreenConstructor<ContainerLogicProgrammerPortable, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenLogicProgrammerPortable::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMaterializer.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.inventory.slot.SlotRemoveOnly;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMaterializer;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\n\nimport java.util.Optional;\n\n/**\n * Container for the materializer.\n * @author rubensworks\n */\npublic class ContainerMaterializer extends ContainerActiveVariableBase<BlockEntityMaterializer> {\n\n    public ContainerMaterializer(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityMaterializer.INVENTORY_SIZE), Optional.empty());\n    }\n\n    public ContainerMaterializer(int id, Inventory playerInventory, Container inventory,\n                          Optional<BlockEntityMaterializer> tileSupplier) {\n        super(RegistryEntries.CONTAINER_MATERIALIZER.get(), id, playerInventory, inventory, tileSupplier);\n        addSlot(new SlotVariable(inventory, BlockEntityProxy.SLOT_READ, 81, 25));\n        addSlot(new SlotVariable(inventory, BlockEntityProxy.SLOT_WRITE_IN, 56, 78));\n        addSlot(new SlotRemoveOnly(inventory, BlockEntityProxy.SLOT_WRITE_OUT, 104, 78));\n        addPlayerInventory(playerInventory, offsetX + 9, offsetY + 107);\n        getTileSupplier().ifPresent(tile -> tile.setLastPlayer(playerInventory.player));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMaterializerConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerMaterializer}.\n * @author rubensworks\n */\npublic class ContainerMaterializerConfig extends GuiConfigCommon<ContainerMaterializer, IModBase> {\n\n    public ContainerMaterializerConfig() {\n        super(IntegratedDynamics._instance,\n                \"materializer\",\n                eConfig -> new MenuType<>(ContainerMaterializer::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerMaterializer> getScreenFactoryProvider() {\n        return new ContainerMaterializerConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMaterializerConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenMaterializer;\n\n/**\n * @author rubensworks\n */\npublic class ContainerMaterializerConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerMaterializer> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerMaterializer>> MenuScreens.ScreenConstructor<ContainerMaterializer, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenMaterializer::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMechanicalDryingBasin.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.Slot;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.inventory.slot.SlotRemoveOnly;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalDryingBasin;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMechanicalMachine;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * Container for the mechanical drying basin.\n * @author rubensworks\n */\npublic class ContainerMechanicalDryingBasin extends ContainerMechanicalMachine<BlockEntityMechanicalDryingBasin> {\n\n    private final Supplier<FluidStack> variableInputFluidStack;\n    private final Supplier<Integer> variableInputFluidCapacity;\n    private final Supplier<FluidStack> variableOutputFluidStack;\n    private final Supplier<Integer> variableOutputFluidCapacity;\n\n    public ContainerMechanicalDryingBasin(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityMechanicalDryingBasin.INVENTORY_SIZE), Optional.empty());\n    }\n\n    public ContainerMechanicalDryingBasin(int id, Inventory playerInventory, Container inventory,\n                                          Optional<BlockEntityMechanicalDryingBasin> tileSupplier) {\n        super(RegistryEntries.CONTAINER_MECHANICAL_DRYING_BASIN.get(), id, playerInventory, inventory, tileSupplier);\n\n        this.variableInputFluidStack = registerSyncedVariable(FluidStack.class, () -> getTileSupplier().get().getTankInput().getFluid());\n        this.variableInputFluidCapacity = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getTankInput().getCapacity());\n        this.variableOutputFluidStack = registerSyncedVariable(FluidStack.class, () -> getTileSupplier().get().getTankOutput().getFluid());\n        this.variableOutputFluidCapacity = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getTankOutput().getCapacity());\n\n        addSlot(new Slot(inventory, 0, 54, 37));\n\n        addSlot(new SlotRemoveOnly(inventory, 1, 108, 29));\n        addSlot(new SlotRemoveOnly(inventory, 2, 126, 29));\n        addSlot(new SlotRemoveOnly(inventory, 3, 108, 47));\n        addSlot(new SlotRemoveOnly(inventory, 4, 126, 47));\n\n        addPlayerInventory(playerInventory, offsetX + 8, offsetY + 86);\n    }\n\n    @Nullable\n    public FluidStack getInputFluidStack() {\n        return variableInputFluidStack.get();\n    }\n\n    public int getInputFluidCapacity() {\n        return variableInputFluidCapacity.get();\n    }\n\n    @Nullable\n    public FluidStack getOutputFluidStack() {\n        return variableOutputFluidStack.get();\n    }\n\n    public int getOutputFluidCapacity() {\n        return variableOutputFluidCapacity.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMechanicalDryingBasinConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerMechanicalDryingBasin}.\n * @author rubensworks\n */\npublic class ContainerMechanicalDryingBasinConfig extends GuiConfigCommon<ContainerMechanicalDryingBasin, IModBase> {\n\n    public ContainerMechanicalDryingBasinConfig() {\n        super(IntegratedDynamics._instance,\n                \"mechanical_drying_basin\",\n                eConfig -> new MenuType<>(ContainerMechanicalDryingBasin::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerMechanicalDryingBasin> getScreenFactoryProvider() {\n        return new ContainerMechanicalDryingBasinConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMechanicalDryingBasinConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenMechanicalDryingBasin;\n\n/**\n * @author rubensworks\n */\npublic class ContainerMechanicalDryingBasinConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerMechanicalDryingBasin> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerMechanicalDryingBasin>> MenuScreens.ScreenConstructor<ContainerMechanicalDryingBasin, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenMechanicalDryingBasin::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMechanicalSqueezer.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.Slot;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.inventory.slot.SlotRemoveOnly;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalSqueezer;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMechanicalMachine;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * Container for the mechanical squeezer.\n * @author rubensworks\n */\npublic class ContainerMechanicalSqueezer extends ContainerMechanicalMachine<BlockEntityMechanicalSqueezer> {\n\n    public static final String BUTTON_TOGGLE_FLUID_EJECT = \"button_eject\";\n\n    private final Supplier<FluidStack> variableFluidStack;\n    private final Supplier<Integer> variableFluidCapacity;\n    private final Supplier<Boolean> variableAutoEject;\n\n    public ContainerMechanicalSqueezer(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityMechanicalSqueezer.INVENTORY_SIZE), Optional.empty());\n    }\n\n    public ContainerMechanicalSqueezer(int id, Inventory playerInventory, Container inventory,\n                                          Optional<BlockEntityMechanicalSqueezer> tileSupplier) {\n        super(RegistryEntries.CONTAINER_MECHANICAL_SQUEEZER.get(), id, playerInventory, inventory, tileSupplier);\n\n        this.variableFluidStack = registerSyncedVariable(FluidStack.class, () -> getTileSupplier().get().getTank().getFluid());\n        this.variableFluidCapacity = registerSyncedVariable(Integer.class, () -> getTileSupplier().get().getTank().getCapacity());\n        this.variableAutoEject = registerSyncedVariable(Boolean.class, () -> getTileSupplier().get().isAutoEjectFluids());\n\n        addSlot(new Slot(inventory, 0, 44, 37));\n\n        addSlot(new SlotRemoveOnly(inventory, 1, 98, 29));\n        addSlot(new SlotRemoveOnly(inventory, 2, 116, 29));\n        addSlot(new SlotRemoveOnly(inventory, 3, 98, 47));\n        addSlot(new SlotRemoveOnly(inventory, 4, 116, 47));\n\n        addPlayerInventory(playerInventory, offsetX + 8, offsetY + 86);\n\n        putButtonAction(BUTTON_TOGGLE_FLUID_EJECT,\n                (buttonId, container) -> getTileSupplier().ifPresent(tile -> tile.setAutoEjectFluids(!getTileSupplier().get().isAutoEjectFluids())));\n    }\n\n    @Nullable\n    public FluidStack getFluidStack() {\n        return variableFluidStack.get();\n    }\n\n    public int getFluidCapacity() {\n        return variableFluidCapacity.get();\n    }\n\n    public boolean isAutoEjectFluids() {\n        return variableAutoEject.get();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMechanicalSqueezerConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerMechanicalSqueezer}.\n * @author rubensworks\n */\npublic class ContainerMechanicalSqueezerConfig extends GuiConfigCommon<ContainerMechanicalSqueezer, IModBase> {\n\n    public ContainerMechanicalSqueezerConfig() {\n        super(IntegratedDynamics._instance,\n                \"mechanical_squeezer\",\n                eConfig -> new MenuType<>(ContainerMechanicalSqueezer::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerMechanicalSqueezer> getScreenFactoryProvider() {\n        return new ContainerMechanicalSqueezerConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerMechanicalSqueezerConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenMechanicalSqueezer;\n\n/**\n * @author rubensworks\n */\npublic class ContainerMechanicalSqueezerConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerMechanicalSqueezer> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerMechanicalSqueezer>> MenuScreens.ScreenConstructor<ContainerMechanicalSqueezer, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenMechanicalSqueezer::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerOnTheDynamicsOfIntegration.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.inventory.ItemLocation;\nimport org.cyclops.cyclopscore.inventory.container.ItemInventoryContainer;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Container for the On the Dynamics of Integration book.\n * @author rubensworks\n */\npublic class ContainerOnTheDynamicsOfIntegration extends ItemInventoryContainer {\n\n    public ContainerOnTheDynamicsOfIntegration(int id, Inventory inventory, FriendlyByteBuf packetBuffer) {\n        this(id, inventory, ItemLocation.readFromPacketBuffer(packetBuffer));\n    }\n\n    public ContainerOnTheDynamicsOfIntegration(int id, Inventory playerInventory, ItemLocation itemLocation) {\n        super(RegistryEntries.CONTAINER_ON_THE_DYNAMICS_OF_INTEGRATION.get(), id, playerInventory, itemLocation);\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerOnTheDynamicsOfIntegrationConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerOnTheDynamicsOfIntegration}.\n * @author rubensworks\n */\npublic class ContainerOnTheDynamicsOfIntegrationConfig extends GuiConfigCommon<ContainerOnTheDynamicsOfIntegration, IModBase> {\n\n    public ContainerOnTheDynamicsOfIntegrationConfig() {\n        super(IntegratedDynamics._instance,\n                \"on_the_dynamics_of_integration\",\n                eConfig -> new ContainerTypeData<>(ContainerOnTheDynamicsOfIntegration::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerOnTheDynamicsOfIntegration> getScreenFactoryProvider() {\n        return new ContainerOnTheDynamicsOfIntegrationConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerOnTheDynamicsOfIntegrationConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenOnTheDynamicsOfIntegration;\n\n/**\n * @author rubensworks\n */\npublic class ContainerOnTheDynamicsOfIntegrationConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerOnTheDynamicsOfIntegration> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerOnTheDynamicsOfIntegration>> MenuScreens.ScreenConstructor<ContainerOnTheDynamicsOfIntegration, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenOnTheDynamicsOfIntegration::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartDisplayConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerPartPanelVariableDriven}.\n * @author rubensworks\n */\npublic class ContainerPartDisplayConfig extends GuiConfigCommon<ContainerPartPanelVariableDriven, IModBase> {\n\n    public ContainerPartDisplayConfig() {\n        super(IntegratedDynamics._instance,\n                \"part_display\",\n                eConfig -> new ContainerTypeData<>(ContainerPartPanelVariableDriven::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerPartPanelVariableDriven> getScreenFactoryProvider() {\n        return new ContainerPartDisplayConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartDisplayConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenPartDisplay;\nimport org.cyclops.integrateddynamics.core.part.panel.PartTypePanelVariableDriven;\n\n/**\n * @author rubensworks\n */\npublic class ContainerPartDisplayConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerPartPanelVariableDriven> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerPartPanelVariableDriven>> MenuScreens.ScreenConstructor<ContainerPartPanelVariableDriven, U> getScreenFactory() {\n        // Due to our use of generics, we have to delegate to a separate function.\n        return new ScreenFactorySafe<>((MenuScreens.ScreenConstructor) createScreenFactory());\n    }\n\n    protected static <P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>> MenuScreens.ScreenConstructor<ContainerPartPanelVariableDriven<P, S>, ContainerScreenPartDisplay<P, S>> createScreenFactory() {\n        return ContainerScreenPartDisplay::new;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartPanelVariableDriven.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.Slot;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipart;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\nimport org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.core.part.event.PartVariableDrivenVariableContentsUpdatedEvent;\nimport org.cyclops.integrateddynamics.core.part.panel.PartTypePanelVariableDriven;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Container for display parts.\n * @author rubensworks\n */\npublic class ContainerPartPanelVariableDriven<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>>\n        extends ContainerMultipart<P, S> {\n\n    private static final int SLOT_X = 79;\n    private static final int SLOT_Y = 8;\n\n    private final int readValueId;\n    private final int readColorId;\n    private final int readErrorsId;\n\n    public ContainerPartPanelVariableDriven(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, new SimpleContainer(packetBuffer.readInt()),\n                Optional.empty(), Optional.empty(), PartHelpers.readPart(packetBuffer));\n    }\n\n    public ContainerPartPanelVariableDriven(int id, Inventory playerInventory, Container inventory,\n                                            Optional<PartTarget> target, Optional<IPartContainer> partContainer, P partType) {\n        super(RegistryEntries.CONTAINER_PART_DISPLAY.get(), id, playerInventory, inventory, target, partContainer, partType);\n\n        readValueId = getNextValueId();\n        readColorId = getNextValueId();\n        readErrorsId = getNextValueId();\n\n        if (inventory instanceof SimpleInventory) {\n            ((SimpleInventory) inventory).addDirtyMarkListener(this);\n        }\n\n        addInventory(inventory, 0, 80, 14, 1, 1);\n        addPlayerInventory(player.getInventory(), 8, 46);\n    }\n\n    @Override\n    protected Slot createNewSlot(Container inventory, int index, int x, int y) {\n        if (inventory instanceof SimpleInventory) {\n            return new SlotVariable(inventory, index, x, y);\n        }\n        return super.createNewSlot(inventory, index, x, y);\n    }\n\n    @Override\n    public void broadcastChanges() {\n        super.broadcastChanges();\n\n        if (!player.level().isClientSide()) {\n            MutableComponent readValue = Component.literal(\"\");\n            int readValueColor = 0;\n            if (!NetworkHelpers.shouldWork()) {\n                readValue = Component.literal(\"SAFE-MODE\");\n            } else {\n                IValue value = getPartState().get().getDisplayValue();\n                if (value != null) {\n                    readValue = value.getType().toCompactString(value);\n                    readValueColor = value.getType().getDisplayColor();\n                }\n            }\n            ValueNotifierHelpers.setValue(this, readValueId, readValue);\n            ValueNotifierHelpers.setValue(this, readColorId, readValueColor);\n            ValueNotifierHelpers.setValue(this, readErrorsId, getPartState().get().getGlobalErrors());\n        }\n    }\n\n    @Override\n    public void onDirty() {\n        if (!player.level().isClientSide()) {\n            S partState = getPartState().get();\n            partState.onVariableContentsUpdated(getPartType(), getTarget().get());\n            Optional<INetwork> optionalNetwork = NetworkHelpers.getNetwork(getTarget().get().getCenter());\n            if (!getContainerInventory().isEmpty()) {\n                    NetworkHelpers.getPartNetwork(optionalNetwork).ifPresent(partNetwork -> {\n                        try {\n                            INetwork network = optionalNetwork.orElse(null);\n                            IVariable variable = partState.getVariable(network, partNetwork, ValueDeseralizationContext.of(player.level()));\n                            NeoForge.EVENT_BUS.post(new PartVariableDrivenVariableContentsUpdatedEvent<>(network, partNetwork, getTarget().get(),\n                                    getPartType(), partState, player, variable, variable != null ? variable.getValue() : null));\n                        } catch (EvaluationException e) {\n\n                        }\n                    });\n\n            }\n            optionalNetwork\n                    .ifPresent(network -> network.getEventBus().post(new VariableContentsUpdatedEvent(network)));\n        }\n    }\n\n    @Override\n    public void removed(Player player) {\n        super.removed(player);\n        if (inventory instanceof SimpleInventory) {\n            ((SimpleInventory) inventory).removeDirtyMarkListener(this);\n        }\n    }\n\n    public Component getReadValue() {\n        return ValueNotifierHelpers.getValueTextComponent(this, readValueId);\n    }\n\n    public int getReadValueColor() {\n        return ValueNotifierHelpers.getValueInt(this, readColorId);\n    }\n\n    public List<Component> getReadErrors() {\n        return ValueNotifierHelpers.getValueTextComponentList(this, readErrorsId);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartReader.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport com.google.common.collect.BiMap;\nimport com.google.common.collect.HashBiMap;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.cyclopscore.inventory.slot.SlotRemoveOnly;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipartAspects;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\nimport org.cyclops.integrateddynamics.core.part.event.PartReaderAspectEvent;\n\nimport java.util.Optional;\n\n/**\n * Container for reader parts.\n * @author rubensworks\n */\npublic class ContainerPartReader<P extends IPartTypeReader<P, S>, S extends IPartStateReader<P>>\n        extends ContainerMultipartAspects<P, S, IAspectRead<?, ?>> {\n\n    public static final int ASPECT_BOX_HEIGHT = 36;\n    private static final int SLOT_IN_X = 96;\n    private static final int SLOT_IN_Y = 27;\n    private static final int SLOT_OUT_X = 144;\n    private static final int SLOT_OUT_Y = 27;\n\n    private final Container outputSlots;\n    private final BiMap<Integer, IAspectRead<?, ?>> readValueIds = HashBiMap.create();\n    private final BiMap<Integer, IAspectRead<?, ?>> readColorIds = HashBiMap.create();\n\n    public ContainerPartReader(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, (RegistryFriendlyByteBuf) packetBuffer);\n    }\n\n    public ContainerPartReader(int id, Inventory playerInventory, RegistryFriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, new SimpleInventory(0, 1),\n                PartHelpers.readPartTarget(packetBuffer), Optional.empty(), PartHelpers.readPart(packetBuffer));\n    }\n\n    public ContainerPartReader(int id, Inventory playerInventory, Container inventory,\n                                PartTarget target, Optional<IPartContainer> partContainer, P partType) {\n        super(RegistryEntries.CONTAINER_PART_READER.get(), id, playerInventory, inventory, target, partContainer, partType,\n                partType.getReadAspects());\n\n        for(int i = 0; i < getUnfilteredItemCount(); i++) {\n            addSlot(new SlotVariable(inputSlots, i, SLOT_IN_X, SLOT_IN_Y + getAspectBoxHeight() * i));\n            disableSlot(i);\n        }\n\n        this.outputSlots = new SimpleInventory(getUnfilteredItemCount(), 1);\n        for(int i = 0; i < getUnfilteredItemCount(); i++) {\n            addSlot(new SlotRemoveOnly(outputSlots, i, SLOT_OUT_X, SLOT_OUT_Y + getAspectBoxHeight() * i));\n            disableSlot(i + getUnfilteredItemCount());\n        }\n\n        addPlayerInventory(player.getInventory(), 9, 131);\n\n        for(IAspectRead<?, ?> aspectRead : getUnfilteredItems()) {\n            readValueIds.put(getNextValueId(), aspectRead);\n            readColorIds.put(getNextValueId(), aspectRead);\n        }\n    }\n\n    @Override\n    protected boolean isAssertInventorySize() {\n        return false;\n    }\n\n    @Override\n    protected int getSizeInventory() {\n        return getPartType().getReadAspects().size() * 2;\n    }\n\n    @Override\n    public int getAspectBoxHeight() {\n        return ASPECT_BOX_HEIGHT;\n    }\n\n    @Override\n    protected void enableSlot(int slotIndex, int row) {\n        Slot slot = getSlot(slotIndex);\n        setSlotPosX(slot, SLOT_IN_X);\n        setSlotPosY(slot, SLOT_IN_Y + getAspectBoxHeight() * row);\n    }\n\n    protected void disableSlotOutput(int slotIndex) {\n        Slot slot = getSlot(slotIndex + getUnfilteredItemCount());\n        // Yes I know this is ugly.\n        // If you are reading this and know a better way, please tell me.\n        setSlotPosX(slot, Integer.MIN_VALUE);\n        setSlotPosY(slot, Integer.MIN_VALUE);\n    }\n\n    protected void enableSlotOutput(int slotIndex, int row) {\n        Slot slot = getSlot(slotIndex + getUnfilteredItemCount());\n        setSlotPosX(slot, SLOT_OUT_X);\n        setSlotPosY(slot, SLOT_OUT_Y + getAspectBoxHeight() * row);\n    }\n\n    @Override\n    public void onScroll(int firstRow) {\n        for(int i = 0; i < getUnfilteredItemCount(); i++) {\n            disableSlotOutput(i);\n        }\n        super.onScroll(firstRow);\n    }\n\n    @Override\n    protected void enableElementAt(int row, int elementIndex, IAspectRead element) {\n        super.enableElementAt(row, elementIndex, element);\n        enableSlotOutput(elementIndex, row);\n    }\n\n    @Override\n    public void removed(Player player) {\n        super.removed(player);\n        if (!player.level().isClientSide()) {\n            for (int i = 0; i < getUnfilteredItemCount(); ++i) {\n                ItemStack itemStack;\n                itemStack = inputSlots.removeItemNoUpdate(i);\n                if (!itemStack.isEmpty()) {\n                    player.drop(itemStack, false);\n                }\n                itemStack = outputSlots.removeItemNoUpdate(i);\n                if (!itemStack.isEmpty()) {\n                    player.drop(itemStack, false);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void onDirty() {\n        for(int i = 0; i < getUnfilteredItemCount(); i++) {\n            ItemStack itemStack = inputSlots.getItem(i);\n            if(!itemStack.isEmpty() && outputSlots.getItem(i).isEmpty()) {\n                ItemStack outputStack = writeAspectInfo(!player.level().isClientSide(), itemStack.copy(), player.level(), getUnfilteredItems().get(i));\n                outputSlots.setItem(i, outputStack);\n                inputSlots.removeItem(i, 1);\n            }\n        }\n    }\n\n    @Override\n    public void broadcastChanges() {\n        super.broadcastChanges();\n\n        try {\n            if (!player.level().isClientSide()) {\n                for (IAspectRead aspectRead : getUnfilteredItems()) {\n                    Pair<MutableComponent, Integer> readValue;\n                    if(getPartState().isEnabled()) {\n                        IVariable variable = getPartType().getVariable(getTarget(), getPartState(), aspectRead);\n                        readValue = ValueHelpers.getSafeReadableValue(variable);\n                    } else {\n                        readValue = Pair.of(Component.literal(\"NO POWER\"), 0);\n                    }\n\n                    setReadValue(aspectRead, readValue);\n                }\n            }\n        } catch (PartStateException e) {\n            player.closeContainer();\n        }\n    }\n\n    public void setReadValue(IAspectRead aspectRead, Pair<MutableComponent, Integer> readValue) {\n        int valueId = readValueIds.inverse().get(aspectRead);\n        int colorId = readColorIds.inverse().get(aspectRead);\n        ValueNotifierHelpers.setValue(this, valueId, readValue.getLeft());\n        ValueNotifierHelpers.setValue(this, colorId, readValue.getRight());\n    }\n\n    public Pair<Component, Integer> getReadValue(IAspectRead aspect) {\n        int valueId = readValueIds.inverse().get(aspect);\n        int colorId = readColorIds.inverse().get(aspect);\n        try {\n            return Pair.of(ValueNotifierHelpers.getValueTextComponent(this, valueId), ValueNotifierHelpers.getValueInt(this, colorId));\n        } catch(NullPointerException e) {\n            return null;\n        }\n    }\n\n    @Override\n    public ItemStack writeAspectInfo(boolean generateId, ItemStack itemStack, Level level, final IAspect aspect) {\n        ItemStack resultStack = super.writeAspectInfo(generateId, itemStack, level, aspect);\n        if (player.level().isClientSide()) {\n            return resultStack;\n        }\n\n        PartTarget target = getTarget();\n        INetwork network = NetworkHelpers.getNetworkChecked(target.getCenter().getPos().getLevel(true),\n                target.getCenter().getPos().getBlockPos(), target.getCenter().getSide());\n        IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);\n        PartReaderAspectEvent event = new PartReaderAspectEvent<>(network, partNetwork, target, getPartType(),\n                getPartState(), player, (IAspectRead) aspect, resultStack);\n        NeoForge.EVENT_BUS.post(event);\n        return event.getItemStack();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartReaderConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerPartReader}.\n * @author rubensworks\n */\npublic class ContainerPartReaderConfig extends GuiConfigCommon<ContainerPartReader<?, ?>, IModBase> {\n\n    public ContainerPartReaderConfig() {\n        super(IntegratedDynamics._instance,\n                \"part_reader\",\n                eConfig -> new ContainerTypeData<>(ContainerPartReader::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerPartReader<?, ?>> getScreenFactoryProvider() {\n        return new ContainerPartReaderConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartReaderConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenPartReader;\n\n/**\n * @author rubensworks\n */\npublic class ContainerPartReaderConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerPartReader<?, ?>> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerPartReader<?, ?>>> MenuScreens.ScreenConstructor<ContainerPartReader<?, ?>, U> getScreenFactory() {\n        // Due to our use of generics, we have to delegate to a separate function.\n        return new ScreenFactorySafe<>((MenuScreens.ScreenConstructor) createScreenFactory());\n    }\n\n    protected static <P extends IPartTypeReader<P, S>, S extends IPartStateReader<P>> MenuScreens.ScreenConstructor<ContainerPartReader<P, S>, ContainerScreenPartReader<P, S>> createScreenFactory() {\n        return ContainerScreenPartReader::new;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartWriter.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport com.google.common.collect.Maps;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.Container;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.Slot;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.ValueNotifierHelpers;\nimport org.cyclops.cyclopscore.inventory.SimpleInventory;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.PartStateException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipartAspects;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * Container for writer parts.\n * @author rubensworks\n */\npublic class ContainerPartWriter<P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>>\n        extends ContainerMultipartAspects<P, S, IAspectWrite<?, ?>> {\n\n    public static final int ASPECT_BOX_HEIGHT = 18;\n    private static final int PAGE_SIZE = 6;\n    private static final int SLOT_X = 131;\n    private static final int SLOT_Y = 18;\n\n    private final int valueId, colorId, enabledId, activeAspectId;\n    private final Map<IAspectWrite<?, ?>, Integer> aspectErrorIds;\n\n    public ContainerPartWriter(int id, Inventory playerInventory, FriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, (RegistryFriendlyByteBuf) packetBuffer);\n    }\n\n    public ContainerPartWriter(int id, Inventory playerInventory, RegistryFriendlyByteBuf packetBuffer) {\n        this(id, playerInventory, new SimpleInventory(packetBuffer.readInt(), 1),\n                PartHelpers.readPartTarget(packetBuffer), Optional.empty(), PartHelpers.readPart(packetBuffer));\n    }\n\n    public ContainerPartWriter(int id, Inventory playerInventory, Container inventory,\n                               PartTarget target, Optional<IPartContainer> partContainer, P partType) {\n        super(RegistryEntries.CONTAINER_PART_WRITER.get(), id, playerInventory, inventory, target, partContainer, partType,\n                partType.getWriteAspects());\n        for(int i = 0; i < getUnfilteredItemCount(); i++) {\n            addSlot(new SlotVariable(inputSlots, i, SLOT_X, SLOT_Y + getAspectBoxHeight() * i));\n            disableSlot(i);\n        }\n\n        addPlayerInventory(player.getInventory(), 9, 140);\n\n        this.valueId = getNextValueId();\n        this.colorId = getNextValueId();\n        this.enabledId = getNextValueId();\n        this.activeAspectId = getNextValueId();\n        this.aspectErrorIds = Maps.newIdentityHashMap();\n        for (IAspectWrite aspect : partType.getWriteAspects()) {\n            this.aspectErrorIds.put(aspect, getNextValueId());\n        }\n    }\n\n    @Override\n    public int getAspectBoxHeight() {\n        return ASPECT_BOX_HEIGHT;\n    }\n\n    @Override\n    public int getPageSize() {\n        return PAGE_SIZE;\n    }\n\n    @Override\n    protected void enableSlot(int slotIndex, int row) {\n        Slot slot = getSlot(slotIndex);\n        setSlotPosX(slot, SLOT_X);\n        setSlotPosY(slot, SLOT_Y + ASPECT_BOX_HEIGHT * row);\n    }\n\n    @Override\n    protected Container constructInputSlotsInventory() {\n        if (!player.level().isClientSide()) {\n            SimpleInventory inventory = getPartState().getInventory();\n            inventory.addDirtyMarkListener(this);\n            return inventory;\n        } else {\n            return super.constructInputSlotsInventory();\n        }\n    }\n\n    @Override\n    public void onDirty() {\n        if (!player.level().isClientSide()) {\n            getPartType().updateActivation(getTarget(), getPartState(), player, false);\n        }\n    }\n\n    @Override\n    public void broadcastChanges() {\n        super.broadcastChanges();\n\n        try {\n            if (!player.level().isClientSide()) {\n                // Update write value\n                Pair<MutableComponent, Integer> readValue;\n                S partState = getPartState();\n                if (!partState.isEnabled()) {\n                    readValue = Pair.of(Component.literal(\"NO POWER\"), 0);\n                } else if (partState.hasVariable()) {\n                    IPartContainer partContainer = getPartContainer();\n                    Optional<INetwork> optionalNetwork = NetworkHelpers.getNetwork(partContainer.getPosition().getLevel(true),\n                            partContainer.getPosition().getBlockPos(), getTarget().getCenter().getSide());\n                    IPartNetwork partNetwork = optionalNetwork.map(NetworkHelpers::getPartNetworkChecked).orElse(null);\n                    if (partNetwork != null) {\n                        IVariable variable = partState.getVariable(optionalNetwork.orElse(null), partNetwork, ValueDeseralizationContext.of(player.level()));\n                        readValue = ValueHelpers.getSafeReadableValue(variable);\n                    } else {\n                        readValue = Pair.of(Component.literal(\"NETWORK CORRUPTED!\"), IModHelpers.get().getBaseHelpers().RGBAToInt(255, 100, 0, 255));\n                    }\n                } else {\n                    readValue = Pair.of(Component.literal(\"\"), 0);\n                }\n                setWriteValue(readValue.getLeft(), readValue.getRight());\n\n                // Update error values\n                for (IAspectWrite<?, ?> aspectWrite : getPartType().getWriteAspects()) {\n                    ValueNotifierHelpers.setValue(this, aspectErrorIds.get(aspectWrite), getPartState().getErrors(aspectWrite));\n                }\n\n                // Update state\n                ValueNotifierHelpers.setValue(this, enabledId, partState.isEnabled());\n                ValueNotifierHelpers.setValue(this, activeAspectId, partState.getActiveAspect() != null ? partState.getActiveAspect().getUniqueName().toString() : \"\");\n            }\n        } catch (PartStateException e) {\n            player.closeContainer();\n        }\n    }\n\n    public void setWriteValue(MutableComponent writeValue, int writeColor) {\n        ValueNotifierHelpers.setValue(this, valueId, writeValue);\n        ValueNotifierHelpers.setValue(this, colorId, writeColor);\n    }\n\n    public Component getWriteValue() {\n        Component value = ValueNotifierHelpers.getValueTextComponent(this, valueId);\n        if(value == null) {\n            value = Component.literal(\"\");\n        }\n        return value;\n    }\n\n    public int getWriteValueColor() {\n        return ValueNotifierHelpers.getValueInt(this, colorId);\n    }\n\n    public List<Component> getAspectErrors(IAspectWrite aspectWrite) {\n        return ValueNotifierHelpers.getValueTextComponentList(this, aspectErrorIds.get(aspectWrite));\n    }\n\n    public boolean isPartStateEnabled() {\n        return ValueNotifierHelpers.getValueBoolean(this, enabledId);\n    }\n\n    @Nullable\n    public IAspect getPartStateActiveAspect() {\n        String aspectName = ValueNotifierHelpers.getValueString(this, activeAspectId);\n        if (aspectName == null) {\n            return null;\n        }\n        return AspectRegistry.getInstance().getAspect(Identifier.parse(aspectName));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartWriterConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.inventory.container.ContainerTypeData;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerPartWriter}.\n * @author rubensworks\n */\npublic class ContainerPartWriterConfig extends GuiConfigCommon<ContainerPartWriter, IModBase> {\n\n    public ContainerPartWriterConfig() {\n        super(IntegratedDynamics._instance,\n                \"part_writer\",\n                eConfig -> new ContainerTypeData<>(ContainerPartWriter::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerPartWriter> getScreenFactoryProvider() {\n        return new ContainerPartWriterConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerPartWriterConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenPartWriter;\n\n/**\n * @author rubensworks\n */\npublic class ContainerPartWriterConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerPartWriter> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerPartWriter>> MenuScreens.ScreenConstructor<ContainerPartWriter, U> getScreenFactory() {\n        // Due to our use of generics, we have to delegate to a separate function.\n        return new ScreenFactorySafe<>((MenuScreens.ScreenConstructor) createScreenFactory());\n    }\n\n    protected static <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> MenuScreens.ScreenConstructor<ContainerPartWriter<P, S>, ContainerScreenPartWriter<P, S>> createScreenFactory() {\n        return ContainerScreenPartWriter::new;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerProxy.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport org.cyclops.cyclopscore.inventory.slot.SlotRemoveOnly;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerActiveVariableBase;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\n\nimport java.util.Optional;\n\n/**\n * Container for the proxy.\n * @author rubensworks\n */\npublic class ContainerProxy extends ContainerActiveVariableBase<BlockEntityProxy> {\n\n    public ContainerProxy(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityProxy.INVENTORY_SIZE), Optional.empty());\n    }\n\n    public ContainerProxy(int id, Inventory playerInventory, Container inventory,\n                          Optional<BlockEntityProxy> tileSupplier) {\n        super(RegistryEntries.CONTAINER_PROXY.get(), id, playerInventory, inventory, tileSupplier);\n        addSlot(new SlotVariable(inventory, BlockEntityProxy.SLOT_READ, 81, 25));\n        addSlot(new SlotVariable(inventory, BlockEntityProxy.SLOT_WRITE_IN, 56, 78));\n        addSlot(new SlotRemoveOnly(inventory, BlockEntityProxy.SLOT_WRITE_OUT, 104, 78));\n        addPlayerInventory(playerInventory, offsetX + 9, offsetY + 107);\n        getTileSupplier().ifPresent(tile -> tile.setLastPlayer(playerInventory.player));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerProxyConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerProxy}.\n * @author rubensworks\n */\npublic class ContainerProxyConfig extends GuiConfigCommon<ContainerProxy, IModBase> {\n\n    public ContainerProxyConfig() {\n        super(IntegratedDynamics._instance,\n                \"proxy\",\n                eConfig -> new MenuType<>(ContainerProxy::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerProxy> getScreenFactoryProvider() {\n        return new ContainerProxyConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerProxyConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenProxy;\n\n/**\n * @author rubensworks\n */\npublic class ContainerProxyConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerProxy> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerProxy>> MenuScreens.ScreenConstructor<ContainerProxy, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenProxy::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerVariablestore.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.Slot;\nimport org.cyclops.cyclopscore.inventory.container.InventoryContainer;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\n\n/**\n * Container for the variablestore.\n * @author rubensworks\n */\npublic class ContainerVariablestore extends InventoryContainer {\n\n    public ContainerVariablestore(int id, Inventory playerInventory) {\n        this(id, playerInventory, new SimpleContainer(BlockEntityVariablestore.INVENTORY_SIZE));\n    }\n\n    public ContainerVariablestore(int id, Inventory playerInventory, Container inventory) {\n        super(RegistryEntries.CONTAINER_VARIABLESTORE.get(), id, playerInventory, inventory);\n        addInventory(inventory, 0, offsetX + 8, offsetY + 18, BlockEntityVariablestore.ROWS, BlockEntityVariablestore.COLS);\n        addPlayerInventory(playerInventory, offsetX + 8, offsetY + 14 + BlockEntityVariablestore.ROWS * 18 + 17);\n    }\n\n    @Override\n    public Slot createNewSlot(Container inventory, int index, int row, int column) {\n        if(inventory instanceof Inventory) {\n            return super.createNewSlot(inventory, index, row, column);\n        }\n        return new SlotVariable(inventory, index, row, column);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerVariablestoreConfig.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.inventory.MenuType;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ContainerVariablestore}.\n * @author rubensworks\n */\npublic class ContainerVariablestoreConfig extends GuiConfigCommon<ContainerVariablestore, IModBase> {\n\n    public ContainerVariablestoreConfig() {\n        super(IntegratedDynamics._instance,\n                \"variablestore\",\n                eConfig -> new MenuType<>(ContainerVariablestore::new, FeatureFlags.VANILLA_SET));\n    }\n\n    @Override\n    public GuiConfigScreenFactoryProvider<ContainerVariablestore> getScreenFactoryProvider() {\n        return new ContainerVariablestoreConfigScreenFactoryProvider();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerVariablestoreConfigScreenFactoryProvider.java",
    "content": "package org.cyclops.integrateddynamics.inventory.container;\n\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.MenuAccess;\nimport org.cyclops.cyclopscore.client.gui.ScreenFactorySafe;\nimport org.cyclops.cyclopscore.config.extendedconfig.GuiConfigScreenFactoryProvider;\nimport org.cyclops.integrateddynamics.client.gui.container.ContainerScreenVariablestore;\n\n/**\n * @author rubensworks\n */\npublic class ContainerVariablestoreConfigScreenFactoryProvider extends GuiConfigScreenFactoryProvider<ContainerVariablestore> {\n    @Override\n    public <U extends Screen & MenuAccess<ContainerVariablestore>> MenuScreens.ScreenConstructor<ContainerVariablestore, U> getScreenFactory() {\n        return new ScreenFactorySafe<>(ContainerScreenVariablestore::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/inventory/slot/FurnaceFuelSlotExtended.java",
    "content": "package org.cyclops.integrateddynamics.inventory.slot;\n\nimport net.minecraft.world.Container;\nimport net.minecraft.world.inventory.FurnaceFuelSlot;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.FuelValues;\n\nimport java.util.function.Supplier;\n\n/**\n * A {@link FurnaceFuelSlot} that does not put restrictions on the used tile entity.\n * @author rubensworks\n */\npublic class FurnaceFuelSlotExtended extends Slot {\n\n    private final Supplier<FuelValues> fuelValuesSupplier;\n\n    public FurnaceFuelSlotExtended(Container inventory, int index, int xPosition, int yPosition, Supplier<FuelValues> fuelValuesSupplier) {\n        super(inventory, index, xPosition, yPosition);\n        this.fuelValuesSupplier = fuelValuesSupplier;\n    }\n\n    @Override\n    public boolean mayPlace(ItemStack itemStack) {\n        return itemStack.getBurnTime(null, this.fuelValuesSupplier.get()) > 0 || FurnaceFuelSlot.isBucket(itemStack);\n    }\n\n    @Override\n    public int getMaxStackSize(ItemStack itemStack) {\n        return FurnaceFuelSlot.isBucket(itemStack) ? 1 : super.getMaxStackSize(itemStack);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemBlockCable.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.sounds.SoundSource;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.item.BlockItem;\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.Block;\nimport net.minecraft.world.level.block.SoundType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;\nimport org.cyclops.integrateddynamics.block.BlockCable;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\n/**\n * The item for the cable.\n * @author rubensworks\n */\npublic class ItemBlockCable extends BlockItem {\n\n    private static final List<IUseAction> USE_ACTIONS = Lists.newArrayList();\n\n    public ItemBlockCable(Block block, Item.Properties builder) {\n        super(block, builder);\n    }\n\n    /**\n     * Register a use action for the cable item.\n     * @param useAction The use action.\n     */\n    public static void addUseAction(IUseAction useAction) {\n        USE_ACTIONS.add(useAction);\n    }\n\n    protected boolean checkCableAt(Level world, BlockPos pos, @Nullable Direction side) {\n        if (!CableHelpers.isNoFakeCable(world, pos, side) && CableHelpers.getCable(world, pos, side) != null) {\n            return true;\n        }\n        for (IUseAction useAction : USE_ACTIONS) {\n            if (useAction.canPlaceAt(world, pos)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    protected boolean canPlace(BlockPlaceContext context, BlockState blockState) {\n        Level world = context.getLevel();\n        BlockPos pos = context.getClickedPos();\n        Direction side = context.getClickedFace();\n        BlockPos target = pos.relative(side);\n        // First check if the target is an unreal cable.\n        if(checkCableAt(world, pos, side)) return true;\n        // Then check if the target is covered by an unreal cable at the given side.\n        if(checkCableAt(world, target, side.getOpposite())) return true;\n        // Skips client-side entity collision detection for placing cables.\n        return (!this.mustSurvive() || blockState.canSurvive(context.getLevel(), target));\n    }\n\n    protected boolean attempItemUseTarget(UseOnContext context, BlockPos pos, Direction side, BlockCable blockCable, boolean offsetAdded) {\n        BlockState blockState = context.getLevel().getBlockState(pos);\n        if(!context.getLevel().isEmptyBlock(pos)) {\n            ICableFakeable cable = CableHelpers.getCableFakeable(context.getLevel(), pos, side).orElse(null);\n            if (cable != null && !cable.isRealCable()) {\n                if (!context.getLevel().isClientSide()) {\n                    cable.setRealCable(true);\n                    CableHelpers.updateConnections(context.getLevel(), pos, side);\n                    CableHelpers.onCableAddedByPlayerActual(context.getLevel(), pos, context.getPlayer());\n                }\n                return true;\n            }\n            if(!offsetAdded){\n                for (IUseAction useAction : USE_ACTIONS) {\n                    if (useAction.attempItemUseTarget(context.getItemInHand(), context.getLevel(), pos, blockCable)) {\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    protected void afterItemUse(UseOnContext context, BlockPos pos, BlockCable blockCable, boolean calledSuper) {\n        if(!calledSuper) {\n            playPlaceSound(context.getLevel(), pos);\n            if (context.getItemInHand().getItem() == RegistryEntries.ITEM_CABLE.get()) {\n                context.getItemInHand().shrink(1);\n            }\n        }\n        blockCable.setDisableCollisionBox(false);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static void playPlaceSound(Level world, BlockPos pos) {\n        Block block = RegistryEntries.BLOCK_CABLE.get();\n        SoundType soundType = block.defaultBlockState().getSoundType();\n        world.playLocalSound((double) ((float) pos.getX() + 0.5F), (double) ((float) pos.getY() + 0.5F), (double) ((float) pos.getZ() + 0.5F),\n                soundType.getPlaceSound(), SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F, false);\n    }\n\n    public static void playBreakSound(Level world, BlockPos pos, BlockState blockState) {\n        world.globalLevelEvent(2001, pos, Block.getId(blockState));\n    }\n\n    @Override\n    public InteractionResult useOn(UseOnContext context) {\n        ItemStack itemStack = context.getItemInHand();\n        // Skips server-side entity collision detection for placing cables.\n        // We temporary disable the collision box of the cable so that it can be placed even if an entity is in the way.\n        BlockCable blockCable = (BlockCable) getBlock();\n        blockCable.setDisableCollisionBox(true);\n\n        // Avoid regular block placement when the target is an unreal cable.\n        if(attempItemUseTarget(context, context.getClickedPos(), context.getClickedFace(), blockCable, false)) {\n            afterItemUse(context, context.getClickedPos(), blockCable, false);\n            return InteractionResult.SUCCESS;\n        }\n\n        // Change pos and side when we are targeting a block that is blocked by an unreal cable, so we want to target\n        // the unreal cable.\n        BlockPos posOffset = context.getClickedPos().relative(context.getClickedFace());\n        if(attempItemUseTarget(context, posOffset, context.getClickedFace().getOpposite(), blockCable, true)) {\n            afterItemUse(context, posOffset, blockCable, false);\n            return InteractionResult.SUCCESS;\n        }\n\n        InteractionResult ret = super.useOn(context);\n        afterItemUse(context, context.getClickedPos(), blockCable, true);\n        return ret;\n    }\n\n    public static interface IUseAction {\n\n        /**\n         * Attempt to use the given item.\n         * @param itemStack The item stack that is being used.\n         * @param world The world.\n         * @param pos The position.\n         * @param blockCable The cable block instance.\n         * @return If the use action was applied.\n         */\n        public boolean attempItemUseTarget(ItemStack itemStack, Level world, BlockPos pos, BlockCable blockCable);\n\n        /**\n         * If the block can be placed at the given position.\n         * @param world The world.\n         * @param pos The position.\n         * @return If the block can be placed.\n         */\n        public boolean canPlaceAt(Level world, BlockPos pos);\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemBlockDelay.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.component.TooltipDisplay;\nimport net.minecraft.world.level.block.Block;\nimport org.cyclops.cyclopscore.item.ItemBlockNBT;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.function.Consumer;\n\n/**\n * The item for the delay.\n * @author rubensworks\n */\npublic class ItemBlockDelay extends ItemBlockNBT {\n\n    public ItemBlockDelay(Block block, Properties builder) {\n        super(block, builder);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        if(itemStack.has(RegistryEntries.DATACOMPONENT_PROXY_ID)) {\n            int id = itemStack.get(RegistryEntries.DATACOMPONENT_PROXY_ID);\n            tooltipAdder.accept(Component.translatable(L10NValues.GENERAL_ITEM_ID, id));\n        }\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemBlockProxy.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.component.TooltipDisplay;\nimport net.minecraft.world.level.block.Block;\nimport org.cyclops.cyclopscore.item.ItemBlockNBT;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\n\nimport java.util.function.Consumer;\n\n/**\n * The item for the proxy.\n * @author rubensworks\n */\npublic class ItemBlockProxy extends ItemBlockNBT {\n\n    public ItemBlockProxy(Block block, Properties builder) {\n        super(block, builder);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        if(itemStack.has(RegistryEntries.DATACOMPONENT_PROXY_ID)) {\n            int id = itemStack.get(RegistryEntries.DATACOMPONENT_PROXY_ID);\n            tooltipAdder.accept(Component.translatable(L10NValues.GENERAL_ITEM_ID, id));\n        }\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemBucketLiquidChorusConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.BucketItem;\nimport net.minecraft.world.item.Items;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for a liquid chorus bucket.\n * @author rubensworks\n */\npublic class ItemBucketLiquidChorusConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemBucketLiquidChorusConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"bucket_liquid_chorus\",\n                (eConfig, properties) -> new BucketItem(RegistryEntries.FLUID_LIQUID_CHORUS.get(), properties\n                        .craftRemainder(Items.BUCKET)\n                        .stacksTo(1))\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemBucketMenrilResinConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.BucketItem;\nimport net.minecraft.world.item.Items;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Config for a menril resin bucket.\n * @author rubensworks\n */\npublic class ItemBucketMenrilResinConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemBucketMenrilResinConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"bucket_menril_resin\",\n                (eConfig, properties) -> new BucketItem(RegistryEntries.FLUID_MENRIL_RESIN.get(), properties\n                        .craftRemainder(Items.BUCKET)\n                        .stacksTo(1))\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemCrystalizedChorusChunkConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Chorus Chunk.\n * @author rubensworks\n *\n */\npublic class ItemCrystalizedChorusChunkConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemCrystalizedChorusChunkConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_chorus_chunk\",\n                (eConfig, properties) -> new Item(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemCrystalizedMenrilChunkConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Crystalized Menril Chunk.\n * @author rubensworks\n *\n */\npublic class ItemCrystalizedMenrilChunkConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemCrystalizedMenrilChunkConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"crystalized_menril_chunk\",\n                (eConfig, properties) -> new Item(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemEnhancement.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\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.item.component.TooltipDisplay;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * An enhancement item.\n * @author rubensworks\n */\npublic class ItemEnhancement extends Item {\n\n    private final Type type;\n\n    public ItemEnhancement(Type type, Properties properties) {\n        super(properties);\n        this.type = type;\n    }\n\n    public <P extends IPartType<P, S>, S extends IPartState<P>> InteractionResult applyEnhancement(IPartType<P, S> partType, IPartState<P> partState, ItemStack itemStack, Player player, InteractionHand hand) {\n        switch (this.type) {\n            case OFFSET -> {\n                if (partType.supportsOffsets()) {\n                    int value = getEnhancementValue(itemStack);\n                    int newValue = partState.getMaxOffset() + value;\n                    if (newValue <= GeneralConfig.maxPartOffset) {\n                        if (!player.level().isClientSide()) {\n                            partState.setMaxOffset(newValue);\n                            itemStack.shrink(1);\n                            player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.enhancement_offset.increased\", newValue));\n                        }\n                        return InteractionResult.SUCCESS;\n                    }\n                    player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.enhancement_offset.limit\", GeneralConfig.maxPartOffset));\n                    return InteractionResult.SUCCESS;\n                }\n            }\n        }\n        return InteractionResult.PASS;\n    }\n\n    public int getEnhancementValue(ItemStack itemStack) {\n        return Objects.requireNonNullElse(itemStack.get(RegistryEntries.DATACOMPONENT_PART_ENHANCEMENT), 0);\n    }\n\n    public void setEnhancementValue(ItemStack itemStack, int value) {\n        itemStack.set(RegistryEntries.DATACOMPONENT_PART_ENHANCEMENT, value);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        tooltipAdder.accept(Component.translatable(\"item.integrateddynamics.enhancement_offset.tooltip\", getEnhancementValue(itemStack)).withStyle(ChatFormatting.GRAY));\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n    }\n\n    public static enum Type {\n        OFFSET\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemEnhancementOffsetConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.ItemStack;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.function.Supplier;\n\n/**\n * Config for an offset enhancement.\n * @author rubensworks\n */\npublic class ItemEnhancementOffsetConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemEnhancementOffsetConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"enhancement_offset\",\n                (eConfig, properties) -> new ItemEnhancement(ItemEnhancement.Type.OFFSET, properties)\n        );\n    }\n\n    @Override\n    public Collection<Supplier<ItemStack>> getDefaultCreativeTabEntries() {\n        return Collections.singleton(() -> {\n            ItemStack itemStack = new ItemStack(getInstance());\n            ((ItemEnhancement) getInstance()).setEnhancementValue(itemStack, 4);\n            return itemStack;\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemFacade.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.core.component.DataComponents;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.context.UseOnContext;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.block.IFacadeable;\n\n/**\n * An item that represents a facade of a certain type.\n * @author rubensworks\n */\npublic class ItemFacade extends Item {\n\n    public ItemFacade(Properties properties) {\n        super(properties);\n    }\n\n    public BlockState getFacadeBlock(ItemStack itemStack) {\n        return itemStack.get(RegistryEntries.DATACOMPONENT_FACADE_BLOCK);\n    }\n\n    public ItemStack getFacadeBlockItem(ItemStack itemStack) {\n        BlockState blockState = getFacadeBlock(itemStack);\n        if(blockState != null) {\n            return IModHelpers.get().getBlockHelpers().getItemStackFromBlockState(blockState);\n        }\n        return null;\n    }\n\n    public void writeFacadeBlock(ItemStack itemStack, BlockState blockState) {\n        // Set block data\n        itemStack.set(RegistryEntries.DATACOMPONENT_FACADE_BLOCK, blockState);\n\n        // Append block name to item name\n        MutableComponent itemName = Component.translatable(this.descriptionId);\n        Component suffix = Component.translatable(\"general.integrateddynamics.info.none\")\n                .withStyle(ChatFormatting.ITALIC);\n        ItemStack itemStackInner = getFacadeBlockItem(itemStack);\n        if(itemStackInner != null) {\n            suffix = getFacadeBlockItem(itemStack).getHoverName();\n        }\n        itemName = itemName\n                .append(\" - \")\n                .append(suffix);\n        itemStack.set(DataComponents.ITEM_NAME, itemName);\n    }\n\n    @Override\n    public InteractionResult useOn(UseOnContext context) {\n        ItemStack itemStack = context.getItemInHand();\n        if(!context.getLevel().isClientSide()) {\n            IFacadeable facadeable = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(context.getLevel(), context.getClickedPos(), null, Capabilities.Facadeable.BLOCK).orElse(null);\n            BlockState blockState = getFacadeBlock(itemStack);\n            if(facadeable != null && blockState != null) {\n                // Add facade to existing cable\n                if (!facadeable.hasFacade()) {\n                    facadeable.setFacade(blockState);\n                    ItemBlockCable.playPlaceSound(context.getLevel(), context.getClickedPos());\n                    itemStack.shrink(1);\n                }\n            }\n            return InteractionResult.SUCCESS;\n        }\n        return super.useOn(context);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemFacadeConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the facade.\n * @author rubensworks\n */\npublic class ItemFacadeConfig extends ItemConfigCommon<IntegratedDynamics> {\n\n    public ItemFacadeConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"facade\",\n                (eConfig, properties) -> new ItemFacade(properties)\n        );\n    }\n\n    @Override\n    public ItemClientConfig<IntegratedDynamics> constructItemClientConfig() {\n        return new ItemFacadeConfigClient(this);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemFacadeConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.color.item.ItemTintSource;\nimport net.minecraft.client.multiplayer.ClientLevel;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.entity.LivingEntity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.neoforged.neoforge.client.event.RegisterColorHandlersEvent;\nimport net.neoforged.neoforge.client.event.RegisterItemModelsEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.client.model.ItemModelFacade;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * @author rubensworks\n */\npublic class ItemFacadeConfigClient extends ItemClientConfig<IntegratedDynamics> {\n    public ItemFacadeConfigClient(ItemConfigCommon<IntegratedDynamics> itemConfig) {\n        super(itemConfig);\n        IntegratedDynamics._instance.getModEventBus().addListener(this::onRegisterColors);\n        itemConfig.getMod().getModEventBus().addListener((RegisterItemModelsEvent event) -> event.register(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"facade\"), ItemModelFacade.Unbaked.MAP_CODEC));\n    }\n\n    public void onRegisterColors(RegisterColorHandlersEvent.ItemTintSources event) {\n        event.register(getItemConfig().getResourceKey().identifier(), Color.MAP_CODEC);\n    }\n\n    public static class Color implements ItemTintSource {\n        public static final MapCodec<Color> MAP_CODEC = RecordCodecBuilder.mapCodec((instance) -> instance.point(new Color()));\n\n        @Override\n        public int calculate(ItemStack itemStack, @Nullable ClientLevel level, @Nullable LivingEntity entity) {\n            BlockState blockstate = ((ItemFacade) itemStack.getItem()).getFacadeBlock(itemStack);\n            if (blockstate == null) {\n                return -1;\n            }\n            net.minecraft.client.color.block.BlockTintSource tintSource = Minecraft.getInstance().getBlockColors().getTintSource(blockstate, 0);\n            if (tintSource != null) {\n                return tintSource.color(blockstate);\n            }\n            return -1;\n        }\n\n        @Override\n        public MapCodec<? extends ItemTintSource> type() {\n            return MAP_CODEC;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemLabeller.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.inventory.ItemLocation;\nimport org.cyclops.cyclopscore.inventory.container.NamedContainerProviderItem;\nimport org.cyclops.cyclopscore.item.ItemGui;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLabeller;\n\nimport javax.annotation.Nullable;\n\n/**\n * A labeller for variables.\n * @author rubensworks\n */\npublic class ItemLabeller extends ItemGui {\n\n    public ItemLabeller(Properties properties) {\n        super(properties);\n    }\n\n    @Nullable\n    @Override\n    public MenuProvider getContainer(Level world, Player playerEntity, ItemLocation itemLocation) {\n        return new NamedContainerProviderItem(itemLocation, itemLocation.getItemStack(playerEntity).getHoverName(), ContainerLabeller::new);\n    }\n\n    @Override\n    public Class<? extends AbstractContainerMenu> getContainerClass(Level world, Player playerEntity, ItemStack itemStack) {\n        return ContainerLabeller.class;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemLabellerConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the labeller.\n * @author rubensworks\n */\npublic class ItemLabellerConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemLabellerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"labeller\",\n                (eConfig, properties) -> new ItemLabeller(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemLogicDirectorConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Logic Director.\n * @author rubensworks\n *\n */\npublic class ItemLogicDirectorConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemLogicDirectorConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"logic_director\",\n                (eConfig, properties) -> new Item(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemMenrilBerriesConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.effect.MobEffectInstance;\nimport net.minecraft.world.effect.MobEffects;\nimport net.minecraft.world.food.FoodProperties;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.component.Consumable;\nimport net.minecraft.world.item.component.Consumables;\nimport net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;\nimport net.minecraft.world.level.block.ComposterBlock;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Menril Berries.\n * @author rubensworks\n *\n */\npublic class ItemMenrilBerriesConfig extends ItemConfigCommon<IModBase> {\n\n    @ConfigurablePropertyCommon(category = \"item\", comment = \"If the berries should give the night vision effect when eaten.\", requiresMcRestart = true)\n    public static boolean nightVision = true;\n\n    public ItemMenrilBerriesConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril_berries\",\n                (eConfig, properties) -> new Item(properties\n                        .food(createFood(), createConsumable()))\n        );\n    }\n\n    protected static FoodProperties createFood() {\n        return new FoodProperties.Builder()\n                .nutrition(4)\n                .saturationModifier(0.3F)\n                .build();\n    }\n\n    protected static Consumable createConsumable() {\n        Consumable.Builder builder = Consumables.defaultFood()\n                .consumeSeconds(0.8F);\n        if (nightVision) {\n            builder = builder.onConsume(new ApplyStatusEffectsConsumeEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 20, 1), 1));\n        }\n        return builder.build();\n    }\n\n    @Override\n    public void onRegistryRegistered() {\n        super.onRegistryRegistered();\n        ComposterBlock.COMPOSTABLES.put(getInstance(), 0.65F);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemOnTheDynamicsOfIntegration.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.common.NeoForge;\nimport net.neoforged.neoforge.event.entity.player.PlayerEvent;\nimport org.cyclops.cyclopscore.inventory.ItemLocation;\nimport org.cyclops.cyclopscore.inventory.container.NamedContainerProviderItem;\nimport org.cyclops.cyclopscore.item.ItemGui;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.entity.item.EntityItemTargetted;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerOnTheDynamicsOfIntegration;\n\nimport javax.annotation.Nullable;\n\n/**\n * On the Dynamics of Integration book.\n * @author rubensworks\n */\npublic class ItemOnTheDynamicsOfIntegration extends ItemGui {\n\n    private static final int SPAWN_RANGE = 25;\n\n    public ItemOnTheDynamicsOfIntegration(Properties properties) {\n        super(properties);\n        NeoForge.EVENT_BUS.addListener(this::onPlayerLoggedIn);\n    }\n\n    @Nullable\n    @Override\n    public MenuProvider getContainer(Level world, Player playerEntity, ItemLocation itemLocation) {\n        return new NamedContainerProviderItem(itemLocation,\n                Component.translatable(\"gui.cyclopscore.infobook\"), ContainerOnTheDynamicsOfIntegration::new);\n    }\n\n    @Override\n    public Class<? extends AbstractContainerMenu> getContainerClass(Level world, Player playerEntity, ItemStack itemStack) {\n        return ContainerOnTheDynamicsOfIntegration.class;\n    }\n\n    private static final String NBT_INFOBOOK_SPAWNED = Reference.MOD_ID + \":infoBookSpawned\";\n    @SubscribeEvent\n    public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {\n        if (ItemOnTheDynamicsOfIntegrationConfig.obtainOnSpawn) {\n            CompoundTag tag = event.getEntity().getPersistentData();\n            if (!tag.contains(Player.PERSISTED_NBT_TAG)) {\n                tag.put(Player.PERSISTED_NBT_TAG, new CompoundTag());\n            }\n            CompoundTag playerTag = tag.getCompound(Player.PERSISTED_NBT_TAG).orElseThrow();\n            if (!playerTag.contains(NBT_INFOBOOK_SPAWNED)) {\n                playerTag.putBoolean(NBT_INFOBOOK_SPAWNED, true);\n\n                Level world = event.getEntity().level();\n                Player player = event.getEntity();\n                ItemStack itemStack = new ItemStack(RegistryEntries.ITEM_ON_THE_DYNAMICS_OF_INTEGRATION);\n                EntityItemTargetted entity = new EntityItemTargetted(world,\n                        player.blockPosition().getX() + SPAWN_RANGE - 2 * SPAWN_RANGE * world.getRandom().nextFloat(),\n                        player.blockPosition().getY() + SPAWN_RANGE * world.getRandom().nextFloat(),\n                        player.blockPosition().getZ() + SPAWN_RANGE - 2 * SPAWN_RANGE * world.getRandom().nextFloat()\n                );\n\n                entity.setItem(itemStack);\n                entity.setTarget(player);\n                world.addFreshEntity(entity);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemOnTheDynamicsOfIntegrationConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.Rarity;\nimport org.cyclops.cyclopscore.config.ConfigurablePropertyCommon;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the infobook.\n * @author rubensworks\n */\npublic class ItemOnTheDynamicsOfIntegrationConfig extends ItemConfigCommon<IModBase> {\n\n    @ConfigurablePropertyCommon(category = \"item\", comment = \"If the info book can give item rewards for tutorial completion.\")\n    public static boolean bookRewards = true;\n\n    @ConfigurablePropertyCommon(category = \"item\", comment = \"If the info book should automatically obtained when the player first spawns.\")\n    public static boolean obtainOnSpawn = true;\n\n    public ItemOnTheDynamicsOfIntegrationConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"on_the_dynamics_of_integration\",\n                (eConfig, properties) -> new ItemOnTheDynamicsOfIntegration(properties\n                        .stacksTo(1)\n                        .rarity(Rarity.UNCOMMON))\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemPortableLogicProgrammer.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.inventory.ItemLocation;\nimport org.cyclops.cyclopscore.inventory.container.NamedContainerProviderItem;\nimport org.cyclops.cyclopscore.item.ItemGui;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerPortable;\n\nimport javax.annotation.Nullable;\n\n/**\n * A portable logic programmer.\n * @author rubensworks\n */\npublic class ItemPortableLogicProgrammer extends ItemGui {\n\n    public ItemPortableLogicProgrammer(Properties properties) {\n        super(properties);\n    }\n\n    @Nullable\n    @Override\n    public MenuProvider getContainer(Level world, Player playerEntity, ItemLocation itemLocation) {\n        return new NamedContainerProviderItem(itemLocation, itemLocation.getItemStack(playerEntity).getHoverName(), ContainerLogicProgrammerPortable::new);\n    }\n\n    @Override\n    public Class<? extends AbstractContainerMenu> getContainerClass(Level world, Player playerEntity, ItemStack itemStack) {\n        return ContainerLogicProgrammerPortable.class;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemPortableLogicProgrammerConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the portable logic programmer.\n * @author rubensworks\n */\npublic class ItemPortableLogicProgrammerConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemPortableLogicProgrammerConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"portable_logic_programmer\",\n                (eConfig, properties) -> new ItemPortableLogicProgrammer(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemProtoChorusConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Logic Director.\n * @author rubensworks\n *\n */\npublic class ItemProtoChorusConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemProtoChorusConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"proto_chorus\",\n                (eConfig, properties) -> new Item(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemVariable.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.component.TooltipDisplay;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHolder;\nimport org.cyclops.integrateddynamics.core.item.VariableFacadeHandlerRegistry;\n\nimport java.util.function.Consumer;\n\n/**\n * Item for storing variable references.\n * @author rubensworks\n */\npublic class ItemVariable extends Item {\n\n    public ItemVariable(Item.Properties properties) {\n        super(properties);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        IVariableFacade variableFacade = getVariableFacade(ValueDeseralizationContext.ofClient(), itemStack);\n        variableFacade.appendHoverText(tooltipAdder, context);\n        if (variableFacade != VariableFacadeHandlerRegistry.DUMMY_FACADE && Minecraft.getInstance().player != null && Minecraft.getInstance().player.isCreative()) {\n            tooltipAdder.accept(Component.translatable(\"item.integrateddynamics.variable.warning\"));\n        }\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n    }\n\n    @Override\n    public Component getName(ItemStack itemStack) {\n        IVariableFacade variableFacade = getVariableFacade(ValueDeseralizationContext.ofAllEnabled(), itemStack);\n        String label;\n        if(variableFacade.isValid() && (label = variableFacade.getLabel()) != null) {\n            return Component.literal(label)\n                    .withStyle(ChatFormatting.ITALIC);\n        }\n        return super.getName(itemStack);\n    }\n\n    public IVariableFacade getVariableFacade(ValueDeseralizationContext valueDeseralizationContext, ItemStack itemStack) {\n        IVariableFacadeHolder holder = itemStack.getCapability(Capabilities.VariableFacade.ITEM);\n        if (holder != null) {\n            return holder.getVariableFacade(valueDeseralizationContext);\n        }\n        return VariableFacadeHandlerRegistry.DUMMY_FACADE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemVariableConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.capability.variablefacade.VariableFacadeHolderDefault;\nimport org.jetbrains.annotations.Nullable;\n\n/**\n * Config for a variable item.\n * @author rubensworks\n */\npublic class ItemVariableConfig extends ItemConfigCommon<IntegratedDynamics> {\n\n    public ItemVariableConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"variable\",\n                (eConfig, properties) -> new ItemVariable(properties)\n        );\n        getMod().getModEventBus().addListener(this::registerCapability);\n    }\n\n    @Override\n    public @Nullable ItemClientConfig<IntegratedDynamics> constructItemClientConfig() {\n        return new ItemVariableConfigClient(this);\n    }\n\n    @SubscribeEvent\n    protected void registerCapability(RegisterCapabilitiesEvent event) {\n        event.registerItem(Capabilities.VariableFacade.ITEM, (stack, context) -> new VariableFacadeHolderDefault(stack), getInstance());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemVariableConfigClient.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.resources.Identifier;\nimport net.neoforged.neoforge.client.event.RegisterItemModelsEvent;\nimport net.neoforged.neoforge.client.event.RegisterSpecialModelRendererEvent;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemClientConfig;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.client.render.blockentity.SpecialModelRendererVariableOverlay;\nimport org.cyclops.integrateddynamics.core.client.model.ItemModelVariableOverlay;\n\n/**\n * @author rubensworks\n */\npublic class ItemVariableConfigClient extends ItemClientConfig<IntegratedDynamics> {\n    public ItemVariableConfigClient(ItemConfigCommon<IntegratedDynamics> itemConfig) {\n        super(itemConfig);\n        itemConfig.getMod().getModEventBus().addListener((RegisterSpecialModelRendererEvent event) -> event.register(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"variable_overlay_special\"), SpecialModelRendererVariableOverlay.Unbaked.MAP_CODEC));\n        itemConfig.getMod().getModEventBus().addListener((RegisterItemModelsEvent event) -> event.register(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"variable_overlays\"), ItemModelVariableOverlay.Unbaked.MAP_CODEC));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemVariableTransformerConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport net.minecraft.world.item.Item;\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for the Input and Output Variable Transformer.\n * @author rubensworks\n *\n */\npublic class ItemVariableTransformerConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemVariableTransformerConfig(boolean input) {\n        super(\n                IntegratedDynamics._instance,\n                \"variable_transformer_\" + (input ? \"input\" : \"output\"),\n                (eConfig, properties) -> new Item(properties)\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemWrench.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport com.google.common.collect.Maps;\nimport io.netty.buffer.ByteBuf;\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.codec.ByteBufCodecs;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.util.StringRepresentable;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\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.item.component.TooltipDisplay;\nimport net.minecraft.world.item.context.UseOnContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelReader;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.phys.BlockHitResult;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.helper.CableHelpers;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * The default wrench for this mod.\n * @author rubensworks\n */\npublic class ItemWrench extends Item {\n\n    private static final Map<String, Mode> NAMED_MODES = Maps.newHashMap();\n    private static final Map<Integer, Mode> INT_MODES = Maps.newHashMap();\n\n    public ItemWrench(Properties properties) {\n        super(properties);\n    }\n\n    @Override\n    public boolean doesSneakBypassUse(ItemStack stack, LevelReader world, BlockPos pos, Player player) {\n        return true;\n    }\n\n    @Override\n    public InteractionResult use(Level world, Player player, InteractionHand hand) {\n        ItemStack itemStack = player.getItemInHand(hand);\n        if (player.isSecondaryUseActive() && !world.isClientSide()) {\n            incrementMode(itemStack);\n            player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode\", Component.translatable(getMode(itemStack).getLabel())));\n            return InteractionResult.SUCCESS.heldItemTransformedTo(itemStack);\n        }\n        return super.use(world, player, hand);\n    }\n\n    @Override\n    public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) {\n        ItemStack itemStack = context.getItemInHand();\n        if (context.getPlayer() != null && context.getPlayer().isSecondaryUseActive()) {\n            switch (getMode(itemStack)) {\n                case OFFSET -> {\n                    // Save offset\n                    itemStack.set(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS, context.getClickedPos());\n                    context.getPlayer().sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.saved\", context.getClickedPos().toShortString()));\n                    return InteractionResult.SUCCESS;\n                }\n                case OFFSET_SIDE -> {\n                    // Save offset and side\n                    itemStack.set(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS, context.getClickedPos());\n                    itemStack.set(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_DIRECTION, context.getClickedFace());\n                    context.getPlayer().sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset_side.saved\", context.getClickedPos().toShortString(), context.getClickedFace().getSerializedName()));\n                    return InteractionResult.SUCCESS;\n                }\n                case DEFAULT -> {\n                    if (!CableHelpers.getCable(context.getLevel(), context.getClickedPos(), context.getClickedFace()).isPresent()) {\n                        return InteractionResult.FAIL;\n                    }\n                }\n            }\n        }\n\n        return super.onItemUseFirst(stack, context);\n    }\n\n    @Override\n    public InteractionResult useOn(UseOnContext context) {\n        BlockState blockState = context.getLevel().getBlockState(context.getClickedPos());\n        if (context.getPlayer() != null && context.getPlayer().isSecondaryUseActive()) {\n            return super.useOn(context);\n        }\n\n        ItemStack itemStack = context.getItemInHand();\n        switch (getMode(itemStack)) {\n            case DEFAULT -> {\n                // Rotate block\n                if (context.getClickedFace().getAxis() == Direction.Axis.Y\n                        && blockState.hasProperty(BlockStateProperties.FACING)) {\n                    // If pointing top or bottom, and we can rotate to UP and DOWN, rotate to that direction or opposite\n                    blockState = blockState.setValue(BlockStateProperties.FACING, blockState.getValue(BlockStateProperties.FACING) == Direction.UP ? Direction.DOWN : Direction.UP);\n                } else if (context.getClickedFace().getAxis() != Direction.Axis.Y\n                        && blockState.hasProperty(BlockStateProperties.FACING)\n                        && blockState.getValue(BlockStateProperties.FACING).getAxis() == Direction.Axis.Y) {\n                    // If not pointing top or bottom, and rotation is UP or DOWN, rotate to facing\n                    blockState = blockState.setValue(BlockStateProperties.FACING, context.getClickedFace());\n                } else {\n                    // Otherwise, just call rotate method\n                    blockState = blockState.rotate(context.getLevel(), context.getClickedPos(), Rotation.CLOCKWISE_90);\n                }\n                context.getLevel().setBlockAndUpdate(context.getClickedPos(), blockState);\n            }\n        }\n        return InteractionResult.SUCCESS;\n    }\n\n    public Mode getMode(ItemStack itemStack) {\n        return Objects.requireNonNullElse(itemStack.get(RegistryEntries.DATACOMPONENT_WRENCH_MODE), Mode.DEFAULT);\n    }\n\n    public void setMode(ItemStack itemStack, Mode mode) {\n        itemStack.set(RegistryEntries.DATACOMPONENT_WRENCH_MODE, mode);\n    }\n\n    public void incrementMode(ItemStack itemStack) {\n        Mode mode = getMode(itemStack);\n        int modeId = mode.ordinal();\n        Mode nextMode = Mode.values()[(modeId + 1) % Mode.values().length];\n        setMode(itemStack, nextMode);\n\n        itemStack.remove(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS);\n        itemStack.remove(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_DIRECTION);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack itemStack, Item.TooltipContext context, TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder, TooltipFlag flag) {\n        super.appendHoverText(itemStack, context, tooltipDisplay, tooltipAdder, flag);\n\n        Mode mode = getMode(itemStack);\n        tooltipAdder.accept(Component.translatable(\"item.integrateddynamics.wrench.mode\", Component.translatable(mode.getLabel())));\n        if (itemStack.has(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS)) {\n            tooltipAdder.accept(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.pos\", itemStack.get(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS).toShortString()).withStyle(ChatFormatting.GRAY));\n        }\n        if (itemStack.has(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_DIRECTION)) {\n            tooltipAdder.accept(Component.translatable(\"item.integrateddynamics.wrench.mode.offset_side.side\", itemStack.get(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_DIRECTION).getSerializedName()).withStyle(ChatFormatting.GRAY));\n        }\n        tooltipAdder.accept(Component.translatable(mode.getLabel() + \".info\").withStyle(ChatFormatting.ITALIC, ChatFormatting.GRAY));\n    }\n\n    public <P extends IPartType<P, S>, S extends IPartState<P>> InteractionResult performPartAction(BlockHitResult hit, IPartType<P, S> partType, IPartState<P> partState, ItemStack itemStack, Player player, InteractionHand hand, PartPos center) {\n        Mode mode = getMode(itemStack);\n        switch (mode) {\n            case OFFSET -> {\n                if (itemStack.has(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS)) {\n                    Vec3i offset = determineOffset(hit, itemStack);\n                    if (((IPartType) partType).setTargetOffset(partState, center, offset)) {\n                        player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.success\"));\n                    } else {\n                        player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.fail\"));\n                    }\n                } else {\n                    player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.incomplete\"));\n                }\n                return InteractionResult.SUCCESS;\n            }\n            case OFFSET_SIDE -> {\n                if (itemStack.has(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS) && itemStack.has(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_DIRECTION)) {\n                    Vec3i offset = determineOffset(hit, itemStack);\n                    Direction side = itemStack.get(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_DIRECTION);\n                    if (((IPartType) partType).setTargetOffset(partState, center, offset)) {\n                        ((IPartType) partType).setTargetSideOverride(partState, side);\n                        player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset_side.success\"));\n                    } else {\n                        player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.fail\"));\n                    }\n                } else {\n                    player.sendOverlayMessage(Component.translatable(\"item.integrateddynamics.wrench.mode.offset.incomplete\"));\n                }\n                return InteractionResult.SUCCESS;\n            }\n        }\n        return InteractionResult.PASS;\n    }\n\n    protected Vec3i determineOffset(BlockHitResult hit, ItemStack itemStack) {\n        BlockPos source = hit.getBlockPos().relative(hit.getDirection());\n        BlockPos targetAbs = itemStack.get(RegistryEntries.DATACOMPONENT_WRENCH_TARGET_BLOCKPOS);\n        return new Vec3i(targetAbs.getX() - source.getX(), targetAbs.getY() - source.getY(), targetAbs.getZ() - source.getZ());\n    }\n\n    public static enum Mode implements StringRepresentable {\n        DEFAULT(\"integrateddynamics:default\", \"item.integrateddynamics.wrench.mode.default\"),\n        OFFSET(\"integrateddynamics:offset\", \"item.integrateddynamics.wrench.mode.offset\"),\n        OFFSET_SIDE(\"integrateddynamics:offset_side\", \"item.integrateddynamics.wrench.mode.offset_side\");\n\n        public static final StringRepresentable.EnumCodec<Mode> CODEC = net.minecraft.util.StringRepresentable.fromEnum(Mode::values);\n        public static final StreamCodec<ByteBuf, Mode> STREAM_CODEC = ByteBufCodecs.idMapper(INT_MODES::get, Mode::ordinal);\n\n        private final String name;\n        private final String label;\n\n        private Mode(String name, String label) {\n            this.name = name;\n            this.label = label;\n            NAMED_MODES.put(name, this);\n            INT_MODES.put(ordinal(), this);\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public String getLabel() {\n            return label;\n        }\n\n        @Override\n        public String getSerializedName() {\n            return getName();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/item/ItemWrenchConfig.java",
    "content": "package org.cyclops.integrateddynamics.item;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.ItemConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for a wrench.\n * @author rubensworks\n */\npublic class ItemWrenchConfig extends ItemConfigCommon<IModBase> {\n\n    public ItemWrenchConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"wrench\",\n                (eConfig, properties) -> new ItemWrench(properties\n                        .stacksTo(1))\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/conditions/LootConditionMatchWrench.java",
    "content": "package org.cyclops.integrateddynamics.loot.conditions;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemInstance;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.level.storage.loot.parameters.LootContextParams;\nimport net.minecraft.world.level.storage.loot.predicates.LootItemCondition;\nimport org.cyclops.integrateddynamics.core.helper.WrenchHelpers;\n\n/**\n * A loot condition testing if a wrench is used.\n * @author rubensworks\n */\npublic class LootConditionMatchWrench implements LootItemCondition {\n\n    public static final MapCodec<LootConditionMatchWrench> CODEC = RecordCodecBuilder.mapCodec(b -> b.point(new LootConditionMatchWrench()));\n\n    @Override\n    public boolean test(LootContext lootContext) {\n        ItemInstance itemInstance = lootContext.getOptionalParameter(LootContextParams.TOOL);\n        Entity entity = lootContext.getOptionalParameter(LootContextParams.THIS_ENTITY);\n        BlockPos blockPos = BlockPos.containing(lootContext.getParameter(LootContextParams.ORIGIN));\n        return itemInstance instanceof ItemStack itemStack\n                && entity instanceof Player\n                && WrenchHelpers.isWrench((Player) entity, itemStack, entity.level(), blockPos, null);\n    }\n\n    @Override\n    public MapCodec<? extends LootItemCondition> codec() {\n        return CODEC;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/conditions/LootConditionMatchWrenchConfig.java",
    "content": "package org.cyclops.integrateddynamics.loot.conditions;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.LootConditionConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class LootConditionMatchWrenchConfig extends LootConditionConfigCommon<IntegratedDynamics> {\n    public LootConditionMatchWrenchConfig() {\n        super(IntegratedDynamics._instance, \"match_wrench\", LootConditionMatchWrench.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyEnergyBatteryData.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;\nimport net.minecraft.world.level.storage.loot.parameters.LootContextParams;\nimport net.minecraft.world.level.storage.loot.predicates.LootItemCondition;\nimport net.neoforged.neoforge.capabilities.Capabilities;\nimport net.neoforged.neoforge.transfer.access.ItemAccess;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityEnergyBattery;\nimport org.cyclops.integrateddynamics.capability.energystorage.IEnergyStorageCapacity;\nimport org.cyclops.integrateddynamics.capability.energystorage.IEnergyStorageMutable;\n\nimport java.util.List;\n\n/**\n * Copies energy battery data to the item.\n * @author rubensworks\n */\npublic class LootFunctionCopyEnergyBatteryData extends LootItemConditionalFunction {\n\n    public static final MapCodec<LootFunctionCopyEnergyBatteryData> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> commonFields(builder).apply(builder, LootFunctionCopyEnergyBatteryData::new)\n    );\n\n    protected LootFunctionCopyEnergyBatteryData(List<LootItemCondition> conditionsIn) {\n        super(conditionsIn);\n    }\n\n    @Override\n    public ItemStack run(ItemStack itemStack, LootContext lootContext) {\n        BlockEntity tile = lootContext.getOptionalParameter(LootContextParams.BLOCK_ENTITY);\n        if (tile instanceof BlockEntityEnergyBattery) {\n            EnergyHandler energyStorage = itemStack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(itemStack));\n            if (energyStorage != null) {\n                ((IEnergyStorageMutable) energyStorage).setEnergy(((BlockEntityEnergyBattery) tile).getEnergyStored());\n                ((IEnergyStorageCapacity) energyStorage).setCapacity(((BlockEntityEnergyBattery) tile).getMaxEnergyStored());\n            }\n        }\n        return itemStack;\n    }\n\n    @Override\n    public MapCodec<? extends LootItemConditionalFunction> codec() {\n        return CODEC;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyEnergyBatteryDataConfig.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.LootFunctionConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class LootFunctionCopyEnergyBatteryDataConfig extends LootFunctionConfigCommon<IModBase> {\n    public LootFunctionCopyEnergyBatteryDataConfig() {\n        super(IntegratedDynamics._instance, \"copy_energy_battery_data\", LootFunctionCopyEnergyBatteryData.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyMechanicalDryingBasinTanks.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;\nimport net.minecraft.world.level.storage.loot.parameters.LootContextParams;\nimport net.minecraft.world.level.storage.loot.predicates.LootItemCondition;\nimport net.neoforged.neoforge.fluids.SimpleFluidContent;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalDryingBasin;\n\nimport java.util.List;\n\n/**\n * Copies the mechanical drying basin tanks.\n * @author rubensworks\n */\npublic class LootFunctionCopyMechanicalDryingBasinTanks extends LootItemConditionalFunction {\n\n    public static final MapCodec<LootFunctionCopyMechanicalDryingBasinTanks> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> commonFields(builder).apply(builder, LootFunctionCopyMechanicalDryingBasinTanks::new)\n    );\n\n    protected LootFunctionCopyMechanicalDryingBasinTanks(List<LootItemCondition> conditionsIn) {\n        super(conditionsIn);\n    }\n\n    @Override\n    public ItemStack run(ItemStack itemStack, LootContext lootContext) {\n        BlockEntity tile = lootContext.getOptionalParameter(LootContextParams.BLOCK_ENTITY);\n        if (tile instanceof BlockEntityMechanicalDryingBasin) {\n            itemStack.set(RegistryEntries.DATACOMPONENT_FLUID_CONTENT_IN_OUT, Pair.of(\n                    SimpleFluidContent.copyOf(((BlockEntityMechanicalDryingBasin) tile).getTankInput().getFluid()),\n                    SimpleFluidContent.copyOf(((BlockEntityMechanicalDryingBasin) tile).getTankOutput().getFluid())\n            ));\n        }\n        return itemStack;\n    }\n\n    @Override\n    public MapCodec<? extends LootItemConditionalFunction> codec() {\n        return CODEC;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyMechanicalDryingBasinTanksConfig.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.LootFunctionConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class LootFunctionCopyMechanicalDryingBasinTanksConfig extends LootFunctionConfigCommon<IModBase> {\n    public LootFunctionCopyMechanicalDryingBasinTanksConfig() {\n        super(IntegratedDynamics._instance, \"copy_mechanical_drying_basin_tanks\", LootFunctionCopyMechanicalDryingBasinTanks.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyMechanicalMachineEnergy.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;\nimport net.minecraft.world.level.storage.loot.parameters.LootContextParams;\nimport net.minecraft.world.level.storage.loot.predicates.LootItemCondition;\nimport org.cyclops.cyclopscore.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;\n\nimport java.util.List;\n\n/**\n * Copies mechanical machine energy.\n * @author rubensworks\n */\npublic class LootFunctionCopyMechanicalMachineEnergy extends LootItemConditionalFunction {\n\n    public static final MapCodec<LootFunctionCopyMechanicalMachineEnergy> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> commonFields(builder).apply(builder, LootFunctionCopyMechanicalMachineEnergy::new)\n    );\n\n    protected LootFunctionCopyMechanicalMachineEnergy(List<LootItemCondition> conditionsIn) {\n        super(conditionsIn);\n    }\n\n    @Override\n    public ItemStack run(ItemStack itemStack, LootContext lootContext) {\n        BlockEntity tile = lootContext.getOptionalParameter(LootContextParams.BLOCK_ENTITY);\n        if (tile instanceof BlockEntityMechanicalMachine) {\n            itemStack.set(RegistryEntries.COMPONENT_ENERGY_STORAGE, ((BlockEntityMechanicalMachine) tile).getEnergyHandler().getAmountAsInt());\n        }\n        return itemStack;\n    }\n\n    @Override\n    public MapCodec<? extends LootItemConditionalFunction> codec() {\n        return CODEC;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyMechanicalMachineEnergyConfig.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.LootFunctionConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class LootFunctionCopyMechanicalMachineEnergyConfig extends LootFunctionConfigCommon<IModBase> {\n    public LootFunctionCopyMechanicalMachineEnergyConfig() {\n        super(IntegratedDynamics._instance, \"copy_mechanical_machine_energy\", LootFunctionCopyMechanicalMachineEnergy.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyMechanicalSqueezerTank.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;\nimport net.minecraft.world.level.storage.loot.parameters.LootContextParams;\nimport net.minecraft.world.level.storage.loot.predicates.LootItemCondition;\nimport net.neoforged.neoforge.fluids.SimpleFluidContent;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMechanicalSqueezer;\n\nimport java.util.List;\n\n/**\n * Copies the mechanical squeezer tank.\n * @author rubensworks\n */\npublic class LootFunctionCopyMechanicalSqueezerTank extends LootItemConditionalFunction {\n\n    public static final MapCodec<LootFunctionCopyMechanicalSqueezerTank> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> commonFields(builder).apply(builder, LootFunctionCopyMechanicalSqueezerTank::new)\n    );\n\n    protected LootFunctionCopyMechanicalSqueezerTank(List<LootItemCondition> conditionsIn) {\n        super(conditionsIn);\n    }\n\n    @Override\n    public ItemStack run(ItemStack itemStack, LootContext lootContext) {\n        BlockEntity tile = lootContext.getOptionalParameter(LootContextParams.BLOCK_ENTITY);\n        if (tile instanceof BlockEntityMechanicalSqueezer) {\n            itemStack.set(org.cyclops.cyclopscore.RegistryEntries.COMPONENT_FLUID_CONTENT, SimpleFluidContent.copyOf(((BlockEntityMechanicalSqueezer) tile).getTank().getFluid()));\n        }\n        return itemStack;\n    }\n\n    @Override\n    public MapCodec<? extends LootItemConditionalFunction> codec() {\n        return CODEC;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyMechanicalSqueezerTankConfig.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.LootFunctionConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class LootFunctionCopyMechanicalSqueezerTankConfig extends LootFunctionConfigCommon<IModBase> {\n    public LootFunctionCopyMechanicalSqueezerTankConfig() {\n        super(IntegratedDynamics._instance, \"copy_mechanical_squeezer_tank\", LootFunctionCopyMechanicalSqueezerTank.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyProxyId.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;\nimport net.minecraft.world.level.storage.loot.parameters.LootContextParams;\nimport net.minecraft.world.level.storage.loot.predicates.LootItemCondition;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\n\nimport java.util.List;\n\n/**\n * Copies a proxy id to the item.\n * @author rubensworks\n */\npublic class LootFunctionCopyProxyId extends LootItemConditionalFunction {\n\n    public static final MapCodec<LootFunctionCopyProxyId> CODEC = RecordCodecBuilder.mapCodec(\n            builder -> commonFields(builder).apply(builder, LootFunctionCopyProxyId::new)\n    );\n\n    protected LootFunctionCopyProxyId(List<LootItemCondition> conditionsIn) {\n        super(conditionsIn);\n    }\n\n    @Override\n    public ItemStack run(ItemStack itemStack, LootContext lootContext) {\n        BlockEntity tile = lootContext.getOptionalParameter(LootContextParams.BLOCK_ENTITY);\n        if (tile instanceof BlockEntityProxy) {\n            itemStack.set(RegistryEntries.DATACOMPONENT_PROXY_ID, ((BlockEntityProxy) tile).getProxyId());\n        }\n        return itemStack;\n    }\n\n    @Override\n    public MapCodec<? extends LootItemConditionalFunction> codec() {\n        return CODEC;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/loot/functions/LootFunctionCopyProxyIdConfig.java",
    "content": "package org.cyclops.integrateddynamics.loot.functions;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.LootFunctionConfigCommon;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class LootFunctionCopyProxyIdConfig extends LootFunctionConfigCommon<IModBase> {\n    public LootFunctionCopyProxyIdConfig() {\n        super(IntegratedDynamics._instance, \"copy_proxy_id\", LootFunctionCopyProxyId.CODEC);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportableAspect.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport org.cyclops.cyclopscore.metadata.IRegistryExportable;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.Collection;\n\n/**\n * Aspect exporter.\n */\npublic class RegistryExportableAspect implements IRegistryExportable {\n\n\n    @Override\n    public JsonObject export() {\n        JsonObject element = new JsonObject();\n\n        for (IAspect aspect : Aspects.REGISTRY.getAspects()) {\n            JsonObject object = new JsonObject();\n\n            object.addProperty(\"id\", aspect.getUniqueName().toString());\n            object.addProperty(\"name\", aspect.getTranslationKey());\n            object.addProperty(\"description\", aspect.getTranslationKey() + \".info\");\n            object.addProperty(\"type\", aspect instanceof IAspectWrite ? \"write\" : \"read\");\n            if (aspect instanceof IAspectWrite) {\n                object.add(\"input\", RegistryExportableOperator.serializeValueType(aspect.getValueType()));\n            } else {\n                object.add(\"output\", RegistryExportableOperator.serializeValueType(aspect.getValueType()));\n            }\n            JsonArray properties = new JsonArray();\n            for (IAspectPropertyTypeInstance propertyType : (Collection<IAspectPropertyTypeInstance>) aspect.getPropertyTypes()) {\n                properties.add(propertyType.getTranslationKey());\n            }\n            object.add(\"properties\", properties);\n\n            element.add(aspect.getUniqueName().toString(), object);\n        }\n\n        return element;\n    }\n\n    @Override\n    public String getName() {\n        return \"aspect\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportableDryingBasinRecipe.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport net.minecraft.core.Holder;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.metadata.IRegistryExportable;\nimport org.cyclops.cyclopscore.metadata.RegistryExportableRecipeAbstract;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeDryingBasin;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Drying basin recipe exporter.\n */\npublic class RegistryExportableDryingBasinRecipe extends RegistryExportableRecipeAbstract<RecipeType<RecipeDryingBasin>, RecipeDryingBasin, IInventoryFluid> {\n\n    protected RegistryExportableDryingBasinRecipe() {\n        super(RegistryEntries.RECIPETYPE_DRYING_BASIN::get);\n    }\n\n    public static JsonObject serializeRecipeStatic(RecipeDryingBasin recipe) {\n        JsonObject object = new JsonObject();\n\n        // Properties\n        int duration = recipe.getDuration();\n        object.addProperty(\"duration\", duration);\n\n        // Inputs\n        JsonObject inputObject = new JsonObject();\n        List<Holder<Item>> inputItems = recipe.getInputIngredient().map(i -> i.items().toList()).orElse(Collections.emptyList());\n        JsonArray arrayInputs = new JsonArray();\n        for (Holder<Item> input : inputItems) {\n            arrayInputs.add(IRegistryExportable.serializeItemStack(new ItemStack(input)));\n        }\n        inputObject.add(\"item\", arrayInputs);\n        Optional<FluidStack> inputFluid = recipe.getInputFluid();\n        inputFluid.ifPresent(fluidStack -> inputObject.add(\"fluid\", IRegistryExportable.serializeFluidStack(fluidStack)));\n\n        // Outputs\n        JsonObject outputObject = new JsonObject();\n        Optional<FluidStack> fluidOutput = recipe.getOutputFluid();\n        fluidOutput.ifPresent(fluidStack -> outputObject.add(\"fluid\", IRegistryExportable.serializeFluidStack(fluidStack)));\n        ItemStack itemOutput = recipe.getOutputItemFirst().orElse(ItemStack.EMPTY);\n        outputObject.add(\"item\", IRegistryExportable.serializeItemStack(itemOutput));\n\n        // Recipe object\n        object.add(\"input\", inputObject);\n        object.add(\"output\", outputObject);\n\n        return object;\n    }\n\n    @Override\n    public JsonObject serializeRecipe(RecipeHolder<RecipeDryingBasin> recipe) {\n        return serializeRecipeStatic(recipe.value());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportableMechanicalDryingBasinRecipe.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonObject;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport org.cyclops.cyclopscore.metadata.RegistryExportableRecipeAbstract;\nimport org.cyclops.cyclopscore.recipe.type.IInventoryFluid;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeMechanicalDryingBasin;\n\n/**\n * Drying basin recipe exporter.\n */\npublic class RegistryExportableMechanicalDryingBasinRecipe extends RegistryExportableRecipeAbstract<RecipeType<RecipeMechanicalDryingBasin>, RecipeMechanicalDryingBasin, IInventoryFluid> {\n\n    protected RegistryExportableMechanicalDryingBasinRecipe() {\n        super(RegistryEntries.RECIPETYPE_MECHANICAL_DRYING_BASIN::get);\n    }\n\n    @Override\n    public JsonObject serializeRecipe(RecipeHolder<RecipeMechanicalDryingBasin> recipe) {\n        return RegistryExportableDryingBasinRecipe.serializeRecipeStatic(recipe.value());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportableMechanicalSqueezerRecipe.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonObject;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport org.cyclops.cyclopscore.metadata.RegistryExportableRecipeAbstract;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeMechanicalSqueezer;\n\n/**\n * Mechanical squeezer recipe exporter.\n */\npublic class RegistryExportableMechanicalSqueezerRecipe extends RegistryExportableRecipeAbstract<RecipeType<RecipeMechanicalSqueezer>, RecipeMechanicalSqueezer, CraftingInput> {\n\n    protected RegistryExportableMechanicalSqueezerRecipe() {\n        super(RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER::get);\n    }\n\n    @Override\n    public JsonObject serializeRecipe(RecipeHolder<RecipeMechanicalSqueezer> recipe) {\n        JsonObject object = RegistryExportableSqueezerRecipe.serializeRecipeStatic(recipe.value());\n\n        // Properties\n        int duration = recipe.value().getDuration();\n        object.addProperty(\"duration\", duration);\n\n        return object;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportableOperator.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport org.cyclops.cyclopscore.metadata.IRegistryExportable;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\n\n/**\n * Operator exporter.\n */\npublic class RegistryExportableOperator implements IRegistryExportable {\n\n\n    @Override\n    public JsonObject export() {\n        JsonObject element = new JsonObject();\n\n        for (IOperator operator : Operators.REGISTRY.getOperators()) {\n            JsonObject object = new JsonObject();\n\n            object.addProperty(\"id\", operator.getUniqueName().toString());\n            object.addProperty(\"name\", operator.getTranslationKey());\n            object.addProperty(\"description\", operator.getTranslationKey() + \".info\");\n            object.addProperty(\"symbol\", operator.getSymbol());\n            JsonArray inputs = new JsonArray();\n            for (IValueType inputType : operator.getInputTypes()) {\n                inputs.add(serializeValueType(inputType));\n            }\n            object.add(\"inputs\", inputs);\n            object.add(\"output\", serializeValueType(operator.getOutputType()));\n            object.addProperty(\"globalInteractName\", operator.getGlobalInteractName());\n            object.addProperty(\"scopedInteractName\", operator.getScopedInteractName());\n\n            element.add(operator.getUniqueName().toString(), object);\n        }\n\n        return element;\n    }\n\n    @Override\n    public String getName() {\n        return \"operator\";\n    }\n\n    protected static JsonObject serializeValueType(IValueType valueType) {\n        JsonObject object = new JsonObject();\n        object.addProperty(\"name\", valueType.getTranslationKey());\n        object.addProperty(\"color\", valueType.getDisplayColorFormat().toString());\n        return object;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportablePartAspect.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport org.cyclops.cyclopscore.metadata.IRegistryExportable;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * Part aspect exporter.\n */\npublic class RegistryExportablePartAspect implements IRegistryExportable {\n\n\n    @Override\n    public JsonObject export() {\n        JsonObject element = new JsonObject();\n\n        for (IPartType partType : PartTypes.REGISTRY.getPartTypes()) {\n            JsonArray array = new JsonArray();\n            for (IAspect aspect : Aspects.REGISTRY.getAspects(partType)) {\n                array.add(aspect.getUniqueName().toString());\n            }\n            element.add(partType.getUniqueName().toString(), array);\n        }\n\n        return element;\n    }\n\n    @Override\n    public String getName() {\n        return \"part_aspect\";\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportableSqueezerRecipe.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport net.minecraft.core.Holder;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.RecipeHolder;\nimport net.minecraft.world.item.crafting.RecipeType;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.cyclops.cyclopscore.metadata.IRegistryExportable;\nimport org.cyclops.cyclopscore.metadata.RegistryExportableRecipeAbstract;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.core.recipe.type.RecipeSqueezer;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * Squeezer recipe exporter.\n */\npublic class RegistryExportableSqueezerRecipe extends RegistryExportableRecipeAbstract<RecipeType<RecipeSqueezer>, RecipeSqueezer, CraftingInput> {\n\n    protected RegistryExportableSqueezerRecipe() {\n        super(RegistryEntries.RECIPETYPE_SQUEEZER::get);\n    }\n\n    public static JsonObject serializeRecipeStatic(RecipeSqueezer recipe) {\n        JsonObject object = new JsonObject();\n\n        // Inputs\n        List<Holder<Item>> inputs = recipe.getInputIngredient().items().toList();\n        JsonArray arrayInputs = new JsonArray();\n        for (Holder<Item> input : inputs) {\n            arrayInputs.add(IRegistryExportable.serializeItemStack(new ItemStack(input)));\n        }\n\n        // Outputs\n        JsonObject outputObject = new JsonObject();\n        Optional<FluidStack> fluidOutput = recipe.getOutputFluid();\n        fluidOutput.ifPresent(fluidStack -> outputObject.add(\"fluid\", IRegistryExportable.serializeFluidStack(fluidStack)));\n        NonNullList<RecipeSqueezer.IngredientChance> itemOutputs = recipe.getOutputItems();\n        JsonArray arrayItemOutputs = new JsonArray();\n        int i = 0;\n        for (RecipeSqueezer.IngredientChance itemOutput : itemOutputs) {\n            JsonObject itemOutputObject = IRegistryExportable.serializeItemStack(itemOutput.getIngredientFirst());\n            float chance = itemOutput.getChance();\n            if (chance > 0) {\n                itemOutputObject.addProperty(\"chance\", chance);\n            }\n            arrayItemOutputs.add(itemOutputObject);\n        }\n        outputObject.add(\"items\", arrayItemOutputs);\n\n        // Recipe object\n        object.add(\"input\", arrayInputs);\n        object.add(\"output\", outputObject);\n\n        return object;\n    }\n\n    @Override\n    public JsonObject serializeRecipe(RecipeHolder<RecipeSqueezer> recipe) {\n        return serializeRecipeStatic(recipe.value());\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/metadata/RegistryExportables.java",
    "content": "package org.cyclops.integrateddynamics.metadata;\n\nimport org.cyclops.cyclopscore.CyclopsCoreNeoForge;\nimport org.cyclops.cyclopscore.metadata.IRegistryExportableRegistry;\n\npublic class RegistryExportables {\n\n    public static IRegistryExportableRegistry REGISTRY = CyclopsCoreNeoForge._instance.getRegistryManager()\n            .getRegistry(IRegistryExportableRegistry.class);\n\n    public static void load() {\n        REGISTRY.register(new RegistryExportableSqueezerRecipe());\n        REGISTRY.register(new RegistryExportableMechanicalSqueezerRecipe());\n        REGISTRY.register(new RegistryExportableDryingBasinRecipe());\n        REGISTRY.register(new RegistryExportableMechanicalDryingBasinRecipe());\n        REGISTRY.register(new RegistryExportableAspect());\n        REGISTRY.register(new RegistryExportableOperator());\n        REGISTRY.register(new RegistryExportablePartAspect());\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/mixin/MixinBlockStateBase.java",
    "content": "package org.cyclops.integrateddynamics.mixin;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.capability.dynamicredstone.DynamicRedstoneHolderGlobal;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n/**\n * @author rubensworks\n */\n@Mixin(BlockBehaviour.BlockStateBase.class)\npublic class MixinBlockStateBase {\n\n    @Inject(at = @At(\"RETURN\"), method = \"Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;getSignal(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Direction;)I\", cancellable = true)\n    public void getSignal(BlockGetter blockGetter, BlockPos pos, Direction facing, CallbackInfoReturnable<Integer> callback) {\n        int originalReturn = callback.getReturnValue();\n        if (originalReturn >= 15) {\n            callback.setReturnValue(originalReturn);\n            return;\n        }\n\n        DynamicRedstoneHolderGlobal instance = DynamicRedstoneHolderGlobal.getInstance();\n        if (instance.hasLevels()) {\n            IDynamicRedstone dynamicRedstone = instance.getDynamicRedstone(DimPos.of((Level) blockGetter, pos), facing);\n            int value = dynamicRedstone.getRedstoneLevel();\n            if (value > 0) {\n                callback.setReturnValue(Math.max(originalReturn, value));\n            }\n        }\n    }\n\n    @Inject(at = @At(\"RETURN\"), method = \"Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;getDirectSignal(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Direction;)I\", cancellable = true)\n    public void getDirectSignal(BlockGetter blockGetter, BlockPos pos, Direction facing, CallbackInfoReturnable<Integer> callback) {\n        int originalReturn = callback.getReturnValue();\n        if (originalReturn >= 15) {\n            callback.setReturnValue(originalReturn);\n            return;\n        }\n\n        DynamicRedstoneHolderGlobal instance = DynamicRedstoneHolderGlobal.getInstance();\n        if (instance.hasLevels()) {\n            IDynamicRedstone dynamicRedstone = instance.getDynamicRedstone(DimPos.of((Level) blockGetter, pos), facing);\n            if (dynamicRedstone.isDirect()) {\n                int value = dynamicRedstone.getRedstoneLevel();\n                if (value > 0) {\n                    callback.setReturnValue(Math.max(originalReturn, value));\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/CoalGeneratorNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedNetworkElement;\nimport org.cyclops.integrateddynamics.core.network.NetworkElementBase;\n\nimport java.util.Objects;\n\n/**\n * Network element for coal generators.\n * @author rubensworks\n */\npublic class CoalGeneratorNetworkElement extends NetworkElementBase implements IPositionedNetworkElement {\n\n    private final DimPos pos;\n\n    public CoalGeneratorNetworkElement(DimPos pos) {\n        this.pos = pos;\n    }\n\n    public DimPos getPos() {\n        return pos;\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n\n    }\n\n    @Override\n    public int getPriority() {\n        return 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public boolean canRevalidate(INetwork network) {\n        return canRevalidatePositioned(network, pos);\n    }\n\n    @Override\n    public void revalidate(INetwork network) {\n        super.revalidate(network);\n        revalidatePositioned(network, pos);\n    }\n\n    @Override\n    public int compareTo(INetworkElement o) {\n        if(o instanceof CoalGeneratorNetworkElement) {\n            return getPos().compareTo(((CoalGeneratorNetworkElement) o).getPos());\n        }\n        return this.getClass().getCanonicalName().compareTo(o.getClass().getCanonicalName());\n    }\n\n    @Override\n    public DimPos getPosition() {\n        return this.pos;\n    }\n\n    @Override\n    public boolean isLoaded() {\n        return INetworkElement.shouldTick(this.getPos());\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        CoalGeneratorNetworkElement that = (CoalGeneratorNetworkElement) o;\n        return Objects.equals(pos, that.pos);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hashCode(pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/DelayNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.Reference;\n\n/**\n * Network element for delays.\n * @author rubensworks\n */\npublic class DelayNetworkElement extends ProxyNetworkElement {\n\n    public static final Identifier GROUP = Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"delay\");\n\n    public DelayNetworkElement(DimPos pos) {\n        super(pos);\n    }\n\n    @Override\n    public Identifier getGroup() {\n        return DelayNetworkElement.GROUP;\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        return GeneralConfig.delayerBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/EnergyBatteryNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.NetworkElementBase;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Network element for variable stores.\n * @author rubensworks\n */\npublic class EnergyBatteryNetworkElement extends NetworkElementBase {\n\n    private final DimPos pos;\n\n    public EnergyBatteryNetworkElement(DimPos pos) {\n        this.pos = pos;\n    }\n\n    public DimPos getPos() {\n        return pos;\n    }\n\n    @Override\n    public int getUpdateInterval() {\n        return 0;\n    }\n\n    @Override\n    public boolean isUpdate() {\n        return false;\n    }\n\n    @Override\n    public void update(INetwork network) {\n\n    }\n\n    @Override\n    public void beforeNetworkKill(INetwork network, @Nullable BlockState blockState, @Nullable BlockEntity blockEntity) {\n\n    }\n\n    @Override\n    public void afterNetworkAlive(INetwork network) {\n\n    }\n\n    @Override\n    public void addDrops(BlockState blockState, BlockEntity blockEntity, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {\n\n    }\n\n    @Override\n    public boolean onNetworkAddition(INetwork network) {\n        PartPos pos = PartPos.of(getPos(), null);\n        boolean added = NetworkHelpers.getEnergyNetworkChecked(network).addPosition(pos, 0, IPositionedAddonsNetwork.DEFAULT_CHANNEL);\n        scheduleNetworkObservation(network, pos);\n        return added;\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, BlockState blockState, BlockEntity blockEntity) {\n        PartPos pos = PartPos.of(getPos(), null);\n        scheduleNetworkObservation(network, pos);\n        NetworkHelpers.getEnergyNetworkChecked(network).removePosition(pos);\n    }\n\n    protected void scheduleNetworkObservation(INetwork network, PartPos pos) {\n        NetworkHelpers.getEnergyNetwork(network)\n                .ifPresent(energyNetwork -> energyNetwork.scheduleObservationForced(IPositionedAddonsNetwork.DEFAULT_CHANNEL, pos));\n    }\n\n    @Override\n    public void onPreRemoved(INetwork network) {\n\n    }\n\n    @Override\n    public void onNeighborBlockChange(@Nullable INetwork network, BlockGetter world, @Nullable Direction side) {\n\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n\n    }\n\n    @Override\n    public int getPriority() {\n        return 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public boolean canRevalidate(INetwork network) {\n        return canRevalidatePositioned(network, pos);\n    }\n\n    @Override\n    public void revalidate(INetwork network) {\n        super.revalidate(network);\n        revalidatePositioned(network, pos);\n    }\n\n    @Override\n    public int compareTo(INetworkElement o) {\n        if(o instanceof EnergyBatteryNetworkElement) {\n            return getPos().compareTo(((EnergyBatteryNetworkElement) o).getPos());\n        }\n        return this.getClass().getCanonicalName().compareTo(o.getClass().getCanonicalName());\n    }\n\n    @Override\n    public boolean isLoaded() {\n        return INetworkElement.shouldTick(this.getPos());\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        EnergyBatteryNetworkElement that = (EnergyBatteryNetworkElement) o;\n        return Objects.equals(pos, that.pos);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hashCode(pos);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/MaterializerNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.network.IEventListenableNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityMaterializer;\nimport org.cyclops.integrateddynamics.core.network.TileNetworkElement;\n\nimport java.util.Optional;\n\n/**\n * Network element for materializers.\n * @author rubensworks\n */\npublic class MaterializerNetworkElement extends TileNetworkElement<BlockEntityMaterializer> implements IEventListenableNetworkElement<BlockEntityMaterializer> {\n\n    public MaterializerNetworkElement(DimPos pos) {\n        super(pos);\n    }\n\n    @Override\n    public Optional<BlockEntityMaterializer> getNetworkEventListener() {\n        return getTile();\n    }\n\n    @Override\n    protected Class<BlockEntityMaterializer> getTileClass() {\n        return BlockEntityMaterializer.class;\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n\n    }\n\n    @Override\n    public int getPriority() {\n        return 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        return GeneralConfig.materializerBaseConsumption;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/MechanicalMachineNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.INetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.core.network.NetworkElementBase;\n\nimport java.util.Objects;\n\n/**\n * Network element for mechanical machines.\n * @author rubensworks\n */\npublic class MechanicalMachineNetworkElement extends NetworkElementBase {\n\n    private final DimPos pos;\n\n    public MechanicalMachineNetworkElement(DimPos pos) {\n        this.pos = pos;\n    }\n\n    public DimPos getPos() {\n        return pos;\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n\n    }\n\n    @Override\n    public int getPriority() {\n        return 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public boolean canRevalidate(INetwork network) {\n        return canRevalidatePositioned(network, pos);\n    }\n\n    @Override\n    public void revalidate(INetwork network) {\n        super.revalidate(network);\n        revalidatePositioned(network, pos);\n    }\n\n    @Override\n    public int compareTo(INetworkElement o) {\n        if(o instanceof MechanicalMachineNetworkElement) {\n            return getPos().compareTo(((MechanicalMachineNetworkElement) o).getPos());\n        }\n        return this.getClass().getCanonicalName().compareTo(o.getClass().getCanonicalName());\n    }\n\n    @Override\n    public boolean isLoaded() {\n        return INetworkElement.shouldTick(this.getPos());\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        MechanicalMachineNetworkElement that = (MechanicalMachineNetworkElement) o;\n        return Objects.equals(pos, that.pos);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hashCode(pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/ProxyNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.network.IEventListenableNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.IIdentifiableNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityProxy;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.TileNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Network element for coal generators.\n * @author rubensworks\n */\npublic class ProxyNetworkElement extends TileNetworkElement<BlockEntityProxy> implements\n        IEventListenableNetworkElement<BlockEntityProxy>, IIdentifiableNetworkElement {\n\n    public static final Identifier GROUP = Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"proxy\");\n\n    public ProxyNetworkElement(DimPos pos) {\n        super(pos);\n    }\n\n    @Override\n    public int getId() {\n        return getTile().get().getProxyId();\n    }\n\n    @Override\n    public Identifier getGroup() {\n        return ProxyNetworkElement.GROUP;\n    }\n\n    @Override\n    public boolean onNetworkAddition(INetwork network) {\n        if(super.onNetworkAddition(network)) {\n            return NetworkHelpers.getPartNetwork(network)\n                    .map(partNetwork -> {\n                        if(!partNetwork.addProxy(getId(), getPos())) {\n                            IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, \"A proxy already existed in the network, this is possibly a \" +\n                                    \"result from item duplication.\");\n                            getTile().get().generateNewProxyId();\n                            return partNetwork.addProxy(getId(), getPos());\n                        }\n                        return true;\n                    })\n                    .orElse(false);\n        }\n        return false;\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, BlockState blockState, BlockEntity blockEntity) {\n        super.onNetworkRemoval(network, blockState, blockEntity);\n        NetworkHelpers.getPartNetwork(network)\n                .ifPresent(partNetwork -> partNetwork.removeProxy(getId()));\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n\n    }\n\n    @Override\n    public int getPriority() {\n        return 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        return GeneralConfig.proxyBaseConsumption;\n    }\n\n    @Nullable\n    @Override\n    public Optional<BlockEntityProxy> getNetworkEventListener() {\n        return getTile();\n    }\n\n    @Override\n    protected Class<BlockEntityProxy> getTileClass() {\n        return BlockEntityProxy.class;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/VariablestoreNetworkElement.java",
    "content": "package org.cyclops.integrateddynamics.network;\n\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.network.IEventListenableNetworkElement;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.blockentity.BlockEntityVariablestore;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.network.TileNetworkElement;\n\nimport javax.annotation.Nullable;\nimport java.util.Optional;\n\n/**\n * Network element for variable stores.\n * @author rubensworks\n */\npublic class VariablestoreNetworkElement extends TileNetworkElement<BlockEntityVariablestore>\n        implements IEventListenableNetworkElement<BlockEntityVariablestore> {\n\n    public VariablestoreNetworkElement(DimPos pos) {\n        super(pos);\n    }\n\n    @Override\n    public boolean onNetworkAddition(INetwork network) {\n        return NetworkHelpers.getPartNetwork(network)\n                .map(partNetwork -> partNetwork.addVariableContainer(getPos()))\n                .orElse(false);\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, BlockState blockState, BlockEntity blockEntity) {\n        NetworkHelpers.getPartNetwork(network)\n                .ifPresent(partNetwork -> partNetwork.removeVariableContainer(getPos()));\n    }\n\n    @Override\n    public void setPriorityAndChannel(INetwork network, int priority, int channel) {\n\n    }\n\n    @Override\n    public int getPriority() {\n        return 0;\n    }\n\n    @Override\n    public int getChannel() {\n        return IPositionedAddonsNetwork.DEFAULT_CHANNEL;\n    }\n\n    @Override\n    public int getConsumptionRate() {\n        return GeneralConfig.variablestoreBaseConsumption;\n    }\n\n    @Override\n    protected Class<BlockEntityVariablestore> getTileClass() {\n        return BlockEntityVariablestore.class;\n    }\n\n    @Nullable\n    @Override\n    public Optional<BlockEntityVariablestore> getNetworkEventListener() {\n        return getTile();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/ActionLabelPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\n\n/**\n * Packet for notifying onLabelPacket changes.\n * @author rubensworks\n *\n */\npublic class ActionLabelPacket extends PacketCodec {\n\n    public static final Type<ActionLabelPacket> TYPE = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"action_label\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, ActionLabelPacket> CODEC = getCodec(ActionLabelPacket::new);\n\n    @CodecField\n    private int variableId;\n    @CodecField\n    private String label; // If null, this action is assumed to be a removal.\n\n    public ActionLabelPacket() {\n        super(TYPE);\n    }\n\n    public ActionLabelPacket(int variableId, String label) {\n        super(TYPE);\n        this.variableId = variableId;\n        this.label = label;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n        if(label == null) {\n            LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().removeUnsafe(variableId);\n        } else {\n            LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().putUnsafe(variableId, label);\n        }\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(label == null) {\n            LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().remove(variableId);\n        } else {\n            LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().put(variableId, label);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/AllLabelsPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\n\nimport java.util.Map;\n\n/**\n * Packet for notifying onLabelPacket changes.\n * @author rubensworks\n *\n */\npublic class AllLabelsPacket extends PacketCodec {\n\n    public static final Type<AllLabelsPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"all_labels\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, AllLabelsPacket> CODEC = getCodec(AllLabelsPacket::new);\n\n    @CodecField\n    private Map<Integer, String> labels;\n\n    public AllLabelsPacket() {\n        super(ID);\n\n    }\n\n    public AllLabelsPacket(Map<Integer, String> labels) {\n        super(ID);\n        this.labels = labels;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n        if (labels != null) {\n            LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().clear();\n            for (Map.Entry<Integer, String> entry : labels.entrySet()) {\n                LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().putUnsafe(entry.getKey(), entry.getValue());\n            }\n        }\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/ItemStackRenamePacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLabeller;\n\n/**\n * Packet for renaming a regular itemstack.\n * @author rubensworks\n *\n */\npublic class ItemStackRenamePacket extends PacketCodec {\n\n    public static final Type<ItemStackRenamePacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"item_stack_rename\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, ItemStackRenamePacket> CODEC = getCodec(ItemStackRenamePacket::new);\n\n    @CodecField\n    private String name;\n\n    public ItemStackRenamePacket() {\n        super(ID);\n    }\n\n    public ItemStackRenamePacket(String name) {\n        super(ID);\n        this.name = name;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLabeller) {\n            ((ContainerLabeller) player.containerMenu).setItemStackName(this.name);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerActivateElementPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for sending a button packet for the exalted crafting.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerActivateElementPacket extends PacketCodec {\n\n    public static final Type<LogicProgrammerActivateElementPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_activate_element\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerActivateElementPacket> CODEC = getCodec(LogicProgrammerActivateElementPacket::new);\n\n    @CodecField\n    private String typeId;\n    @CodecField\n    private String elementId;\n\n    public LogicProgrammerActivateElementPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerActivateElementPacket(Identifier typeId, Identifier elementId) {\n        super(ID);\n        this.typeId = typeId.toString();\n        this.elementId = elementId.toString();\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            Identifier type = Identifier.parse(typeId);\n            if (type.getPath().isEmpty()) {\n                ((ContainerLogicProgrammerBase) player.containerMenu).returnWriteItemToPlayer();\n            }\n            ((ContainerLogicProgrammerBase) player.containerMenu).setActiveElementById(\n                    type, Identifier.parse(elementId));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerLabelPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for sending a button packet to trigger variable labeling.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerLabelPacket extends PacketCodec {\n\n    public static final Type<LogicProgrammerLabelPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_label\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerLabelPacket> CODEC = getCodec(LogicProgrammerLabelPacket::new);\n\n    @CodecField\n    private String label;\n\n    public LogicProgrammerLabelPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerLabelPacket(String label) {\n        super(ID);\n        this.label = label;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ((ContainerLogicProgrammerBase) player.containerMenu).onLabelPacket(label);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerSetElementInventory.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeIngredientsLPElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeListLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for updating the element inventory inside the logic programmer.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerSetElementInventory extends PacketCodec {\n\n    public static final Type<LogicProgrammerSetElementInventory> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_set_element\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerSetElementInventory> CODEC = getCodec(LogicProgrammerSetElementInventory::new);\n\n    @CodecField\n    private String listValueType;\n    @CodecField\n    private int baseX;\n    @CodecField\n    private int baseY;\n\n    public LogicProgrammerSetElementInventory() {\n        super(ID);\n    }\n\n    public LogicProgrammerSetElementInventory(IValueType listValueType, int baseX, int baseY) {\n        super(ID);\n        this.listValueType = listValueType.getUniqueName().toString();\n        this.baseX = baseX;\n        this.baseY = baseY;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ContainerLogicProgrammerBase container = (ContainerLogicProgrammerBase) player.containerMenu;\n            ILogicProgrammerElement element = container.getActiveElement();\n            if (element instanceof ValueTypeListLPElement || element instanceof ValueTypeIngredientsLPElement) {\n                IValueType valueType = ValueTypes.REGISTRY.getValueType(Identifier.parse(this.listValueType));\n                if (valueType != null) {\n                    ((ContainerLogicProgrammerBase) player.containerMenu).setElementInventory(\n                            valueType.createLogicProgrammerElement(), baseX, baseY);\n                } else {\n                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN,\n                            \"Got an invalid LogicProgrammerSetElementInventory packet: \" + this.listValueType);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeBooleanValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeBooleanLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for changing a boolean LP value.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeBooleanValueChangedPacket extends PacketCodec {\n\n    public static final Type<LogicProgrammerValueTypeBooleanValueChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_boolean_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeBooleanValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeBooleanValueChangedPacket::new);\n\n    @CodecField\n    private boolean checked;\n\n    public LogicProgrammerValueTypeBooleanValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeBooleanValueChangedPacket(boolean checked) {\n        super(ID);\n        this.checked = checked;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeBooleanLPElement) {\n                ((ValueTypeBooleanLPElement) element).getInnerGuiElement().setInputBoolean(checked);\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeIngredientsValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeIngredients;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeIngredientsLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.slf4j.Logger;\n\n/**\n * Packet for sending a button packet for a change in current ingredients value.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeIngredientsValueChangedPacket extends PacketCodec<LogicProgrammerValueTypeIngredientsValueChangedPacket> {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    public static final Type<LogicProgrammerValueTypeIngredientsValueChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_ingredients_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeIngredientsValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeIngredientsValueChangedPacket::new);\n\n    @CodecField\n    private CompoundTag value;\n\n    public LogicProgrammerValueTypeIngredientsValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeIngredientsValueChangedPacket(ValueDeseralizationContext valueDeseralizationContext, ValueObjectTypeIngredients.ValueIngredients value) {\n        super(ID);\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new LogicProgrammerValueTypeListValueChangedPacket.PathElement(), LOGGER)) {\n            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n            ValueHelpers.serializeRaw(valueOutput, value);\n            this.value = valueOutput.buildResult();\n        }\n    }\n\n    protected ValueObjectTypeIngredients.ValueIngredients getValue(Level level) {\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new LogicProgrammerValueTypeListValueChangedPacket.PathElement(), LOGGER)) {\n            ValueInput input = TagValueInput.create(\n                    scopedCollector,\n                    level.registryAccess(),\n                    value\n            );\n            return ValueHelpers.deserializeRaw(input, ValueTypes.OBJECT_INGREDIENTS);\n        }\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeIngredientsLPElement) {\n                ((ValueTypeIngredientsLPElement) element).setServerValue(getValue(world));\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeListValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeListLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.slf4j.Logger;\n\n/**\n * Packet for sending a button packet for the exalted crafting.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeListValueChangedPacket extends PacketCodec {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    public static final Type<LogicProgrammerValueTypeListValueChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_list_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeListValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeListValueChangedPacket::new);\n\n    @CodecField\n    private CompoundTag value;\n\n    public LogicProgrammerValueTypeListValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeListValueChangedPacket(ValueDeseralizationContext valueDeseralizationContext, ValueTypeList.ValueList value) {\n        super(ID);\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new PathElement(), LOGGER)) {\n            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n            ValueHelpers.serializeRaw(valueOutput, value);\n            this.value = valueOutput.buildResult();\n        }\n    }\n\n    protected ValueTypeList.ValueList getListValue(Level level) {\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new PathElement(), LOGGER)) {\n            ValueInput input = TagValueInput.create(\n                    scopedCollector,\n                    level.registryAccess(),\n                    value\n            );\n            return ValueHelpers.deserializeRaw(input, ValueTypes.LIST);\n        }\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeListLPElement) {\n                ((ValueTypeListLPElement) element).setServerValue(getListValue(world));\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n    public static record PathElement() implements ProblemReporter.PathElement {\n        @Override\n        public String get() {\n            return \"packet\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeOperatorValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport com.mojang.logging.LogUtils;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.storage.TagValueInput;\nimport net.minecraft.world.level.storage.TagValueOutput;\nimport net.minecraft.world.level.storage.ValueInput;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.operator.IOperator;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeOperatorLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\nimport org.slf4j.Logger;\n\n/**\n * Packet for sending a button packet for the exalted crafting.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeOperatorValueChangedPacket extends PacketCodec {\n\n    private static final Logger LOGGER = LogUtils.getLogger();\n    public static final Type<LogicProgrammerValueTypeOperatorValueChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_operator_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeOperatorValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeOperatorValueChangedPacket::new);\n\n    @CodecField\n    private CompoundTag operatorValue;\n\n    public LogicProgrammerValueTypeOperatorValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeOperatorValueChangedPacket(ValueDeseralizationContext valueDeseralizationContext, ValueTypeOperator.ValueOperator value) {\n        super(ID);\n        try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new LogicProgrammerValueTypeListValueChangedPacket.PathElement(), LOGGER)) {\n            TagValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, valueDeseralizationContext.holderLookupProvider());\n            ValueHelpers.serializeRaw(valueOutput, value);\n            this.operatorValue = valueOutput.buildResult();\n        }\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeOperatorLPElement) {\n                IOperator operator;\n                try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(new LogicProgrammerValueTypeListValueChangedPacket.PathElement(), LOGGER)) {\n                    ValueInput input = TagValueInput.create(\n                            scopedCollector,\n                            world.registryAccess(),\n                            operatorValue\n                    );\n                    operator = ValueHelpers.deserializeRaw(input, ValueTypes.OPERATOR).getRawValue();\n                } catch (IllegalArgumentException e) {\n                    operator = null;\n                }\n                ((ValueTypeOperatorLPElement) element).setSelectedOperator(operator);\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for sending a to the server if recipe slot properties have changed.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket extends PacketCodec {\n\n    public static final Type<LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_recipe_slot_properties_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket::new);\n\n    @CodecField\n    private int slot;\n    @CodecField\n    private boolean nbt;\n    @CodecField\n    private String tag;\n    @CodecField\n    private int tagQuantity;\n    @CodecField\n    private boolean reusable;\n\n    public LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket(int slot, boolean nbt, String tag, int tagQuantity, boolean reusable) {\n        super(ID);\n        this.slot = slot;\n        this.nbt = nbt;\n        this.tag = tag;\n        this.tagQuantity = tagQuantity;\n        this.reusable = reusable;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeRecipeLPElement) {\n                ItemMatchProperties props = ((ValueTypeRecipeLPElement) element).getInputStacks().get(slot);\n                props.setNbt(nbt);\n                props.setItemTag(tag.isEmpty() ? null : tag);\n                props.setTagQuantity(this.tagQuantity);\n                props.setReusable(reusable);\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeRecipeValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.network.protocol.common.custom.CustomPacketPayload;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeRecipeLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for sending a to the server if a recipe string value has changed.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeRecipeValueChangedPacket extends PacketCodec {\n\n    public static final CustomPacketPayload.Type<LogicProgrammerValueTypeRecipeValueChangedPacket> ID = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_recipe_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeRecipeValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeRecipeValueChangedPacket::new);\n\n    @CodecField\n    private String value;\n    @CodecField\n    private int type;\n\n    public LogicProgrammerValueTypeRecipeValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeRecipeValueChangedPacket(String value, Type type) {\n        super(ID);\n        this.value = value;\n        this.type = type.ordinal();\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeRecipeLPElement) {\n                Type type = Type.values()[this.type];\n                switch (type) {\n                    case INPUT_FLUID:\n                        ((ValueTypeRecipeLPElement) element).setInputFluidAmount(value);\n                        break;\n                    case INPUT_ENERGY:\n                        ((ValueTypeRecipeLPElement) element).setInputEnergy(value);\n                        break;\n                    case OUTPUT_FLUID:\n                        ((ValueTypeRecipeLPElement) element).setOutputFluidAmount(value);\n                        break;\n                    case OUTPUT_ENERGY:\n                        ((ValueTypeRecipeLPElement) element).setOutputEnergy(value);\n                        break;\n                }\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n    public static enum Type {\n        INPUT_FLUID,\n        INPUT_ENERGY,\n        OUTPUT_FLUID,\n        OUTPUT_ENERGY,\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeSlottedValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for letting the server know of a logic programmer element itemstack value.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeSlottedValueChangedPacket extends PacketCodec {\n\n    public static final Type<LogicProgrammerValueTypeSlottedValueChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_slotted_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeSlottedValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeSlottedValueChangedPacket::new);\n\n    @CodecField\n    private ItemStack itemStack;\n\n    public LogicProgrammerValueTypeSlottedValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeSlottedValueChangedPacket(ItemStack itemStack) {\n        super(ID);\n        this.itemStack = itemStack;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeLPElementBase) {\n                int slotId = player.containerMenu.slots.size() - 1;\n                player.containerMenu.setItem(slotId, 0, itemStack.copy());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/LogicProgrammerValueTypeStringValueChangedPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeStringLPElement;\nimport org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;\n\n/**\n * Packet for sending a button packet for the exalted crafting.\n * @author rubensworks\n *\n */\npublic class LogicProgrammerValueTypeStringValueChangedPacket extends PacketCodec {\n\n    public static final Type<LogicProgrammerValueTypeStringValueChangedPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"logic_programmer_value_type_string_value_changed\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, LogicProgrammerValueTypeStringValueChangedPacket> CODEC = getCodec(LogicProgrammerValueTypeStringValueChangedPacket::new);\n\n    @CodecField\n    private String value;\n\n    public LogicProgrammerValueTypeStringValueChangedPacket() {\n        super(ID);\n    }\n\n    public LogicProgrammerValueTypeStringValueChangedPacket(String value) {\n        super(ID);\n        this.value = value;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if(player.containerMenu instanceof ContainerLogicProgrammerBase) {\n            ILogicProgrammerElement element = ((ContainerLogicProgrammerBase) player.containerMenu).getActiveElement();\n            if(element instanceof ValueTypeStringLPElement) {\n                ((ValueTypeStringLPElement) element).getInnerGuiElement().setInputString(value);\n                ((ContainerLogicProgrammerBase) player.containerMenu).onDirty();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/NetworkDiagnosticsNetworkPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDataClient;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.RawNetworkData;\n\n/**\n * Packet for subscribing a network update to a player.\n * @author rubensworks\n *\n */\npublic class NetworkDiagnosticsNetworkPacket extends PacketCodec {\n\n    public static final Type<NetworkDiagnosticsNetworkPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"network_diagnostics_network\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, NetworkDiagnosticsNetworkPacket> CODEC = getCodec(NetworkDiagnosticsNetworkPacket::new);\n\n    @CodecField\n    private CompoundTag networkData;\n\n    public NetworkDiagnosticsNetworkPacket() {\n        super(ID);\n    }\n\n    public NetworkDiagnosticsNetworkPacket(CompoundTag networkData) {\n        super(ID);\n        this.networkData = networkData;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n        RawNetworkData networkData = RawNetworkData.fromNbt(this.networkData);\n        if (networkData.getParts().isEmpty()) {\n            // Force observers to be cleared when no parts are present.\n            networkData.getObservers().clear();\n        }\n        NetworkDataClient.setNetworkData(networkData.getId(), networkData.isKilled() ? null : networkData);\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/NetworkDiagnosticsSubscribePacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;\n\n/**\n * Packet for subscribing a player to network diagnostics updates.\n * @author rubensworks\n *\n */\npublic class NetworkDiagnosticsSubscribePacket extends PacketCodec {\n\n    public static final Type<NetworkDiagnosticsSubscribePacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"network_diagnostics_subscribe\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, NetworkDiagnosticsSubscribePacket> CODEC = getCodec(NetworkDiagnosticsSubscribePacket::new);\n\n    @CodecField\n    private boolean subscribe;\n\n    public NetworkDiagnosticsSubscribePacket() {\n        super(ID);\n    }\n\n    public NetworkDiagnosticsSubscribePacket(boolean subscribe) {\n        super(ID);\n        this.subscribe = subscribe;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if (subscribe) {\n            NetworkDiagnostics.getInstance().registerPlayer(player);\n        } else {\n            NetworkDiagnostics.getInstance().unRegisterPlayer(player);\n        }\n    }\n\n    public static NetworkDiagnosticsSubscribePacket subscribe() {\n        return new NetworkDiagnosticsSubscribePacket(true);\n    }\n\n    public static NetworkDiagnosticsSubscribePacket unsubscribe() {\n        return new NetworkDiagnosticsSubscribePacket(false);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/NetworkDiagnosticsTriggerClient.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.chat.ClickEvent;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.Style;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.util.Util;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDataClient;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnosticsPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.http.DiagnosticsWebServer;\nimport org.cyclops.integrateddynamics.proxy.ClientProxy;\n\nimport java.net.URISyntaxException;\n\n/**\n * Packet for opening or closing network diagnostics at a client.\n * @author rubensworks\n *\n */\npublic class NetworkDiagnosticsTriggerClient extends PacketCodec {\n\n    public static final Type<NetworkDiagnosticsTriggerClient> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"network_diagnostics_trigger\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, NetworkDiagnosticsTriggerClient> CODEC = getCodec(NetworkDiagnosticsTriggerClient::new);\n\n    @CodecField\n    private boolean start;\n    @CodecField\n    private int port;\n\n    public NetworkDiagnosticsTriggerClient(boolean start, int port) {\n        super(ID);\n        this.start = start;\n        this.port = port;\n    }\n\n    public NetworkDiagnosticsTriggerClient() {\n        this(true, 0);\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n        if (start) {\n            if (ClientProxy.DIAGNOSTICS_SERVER == null) {\n                IntegratedDynamics._instance.getPacketHandler().sendToServer(NetworkDiagnosticsSubscribePacket.subscribe());\n                new Thread(() -> {\n                    ClientProxy.DIAGNOSTICS_SERVER = new DiagnosticsWebServer(port);\n                    ClientProxy.DIAGNOSTICS_SERVER.initialize();\n                    Minecraft.getInstance().execute(() -> {\n                        try {\n                            player.sendOverlayMessage(\n                                    Component.literal(\"Diagnostics server has been started on \")\n                                            .append(Component.literal(ClientProxy.DIAGNOSTICS_SERVER.getUrl())\n                                                    .setStyle(Style.EMPTY\n                                                            .withUnderlined(true)\n                                                            .withClickEvent(new ClickEvent.OpenUrl(Util.parseAndValidateUntrustedUri(ClientProxy.DIAGNOSTICS_SERVER.getUrl())))))\n                            );\n                        } catch (URISyntaxException e) {\n                            throw new RuntimeException(e);\n                        }\n                    });\n                }).start();\n            } else {\n                Minecraft.getInstance().execute(() -> {\n                    try {\n                        player.sendOverlayMessage(\n                                Component.literal(\"Diagnostics server is already running on \")\n                                        .append(Component.literal(ClientProxy.DIAGNOSTICS_SERVER.getUrl())\n                                                .setStyle(Style.EMPTY\n                                                        .withUnderlined(true)\n                                                        .withClickEvent(new ClickEvent.OpenUrl(Util.parseAndValidateUntrustedUri(ClientProxy.DIAGNOSTICS_SERVER.getUrl())))))\n                        );\n                    } catch (URISyntaxException e) {\n                        throw new RuntimeException(e);\n                    }\n                });\n            }\n        } else {\n            if (ClientProxy.DIAGNOSTICS_SERVER != null) {\n                IntegratedDynamics._instance.getPacketHandler().sendToServer(NetworkDiagnosticsSubscribePacket.unsubscribe());\n                new Thread(() -> {\n                    NetworkDiagnosticsPartOverlayRenderer.getInstance().clearPositions();\n                    NetworkDataClient.clearNetworkData();\n                    ClientProxy.DIAGNOSTICS_SERVER.deinitialize();\n                    ClientProxy.DIAGNOSTICS_SERVER = null;\n                    Minecraft.getInstance().execute(() -> {\n                        player.sendOverlayMessage(Component.literal(\"Stopped diagnostics server\"));\n                    });\n                }).start();\n            } else {\n                Minecraft.getInstance().execute(() -> {\n                    player.sendOverlayMessage(Component.literal(\"No diagnostics server is running\"));\n                });\n            }\n        }\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/PartOffsetsDataPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.cyclopscore.network.PacketCodecs;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.client.render.level.PartOffsetsOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.network.PartOffsetsClientNotifier;\n\nimport java.util.List;\n\n/**\n * Packet for sending part offsets to a player.\n * @author rubensworks\n */\npublic class PartOffsetsDataPacket extends PacketCodec {\n\n    public static final Type<PartOffsetsDataPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"part_offsets_data\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, PartOffsetsDataPacket> CODEC = getCodec(PartOffsetsDataPacket::new);\n\n    static {\n        PacketCodecs.addCodedAction(PartOffsetsClientNotifier.Entry.class, new ICodecAction() {\n            @Override\n            public void encode(Object object, RegistryFriendlyByteBuf output) {\n                PartOffsetsClientNotifier.Entry entry = (PartOffsetsClientNotifier.Entry) object;\n                PacketCodecs.getAction(BlockPos.class).encode(entry.source(), output);\n                PacketCodecs.getAction(Direction.class).encode(entry.sourceSide(), output);\n                PacketCodecs.getAction(Vec3i.class).encode(entry.targetOffset(), output);\n                PacketCodecs.getAction(Direction.class).encode(entry.targetSide(), output);\n            }\n\n            @Override\n            public Object decode(RegistryFriendlyByteBuf input) {\n                return new PartOffsetsClientNotifier.Entry(\n                        (BlockPos) PacketCodecs.getAction(BlockPos.class).decode(input),\n                        (Direction) PacketCodecs.getAction(Direction.class).decode(input),\n                        (Vec3i) PacketCodecs.getAction(Vec3i.class).decode(input),\n                        (Direction) PacketCodecs.getAction(Direction.class).decode(input)\n                );\n            }\n        });\n    }\n\n    @CodecField\n    private List<PartOffsetsClientNotifier.Entry> offsets;\n\n    public PartOffsetsDataPacket() {\n        super(ID);\n    }\n\n    public PartOffsetsDataPacket(List<PartOffsetsClientNotifier.Entry> offsets) {\n        super(ID);\n        this.offsets = offsets;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n        PartOffsetsOverlayRenderer.getInstance().setData(this.offsets);\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/PartOffsetsSubscribePacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.core.network.PartOffsetsClientNotifier;\n\n/**\n * Packet for subscribing a player to network diagnostics updates.\n * @author rubensworks\n *\n */\npublic class PartOffsetsSubscribePacket extends PacketCodec {\n\n    public static final Type<PartOffsetsSubscribePacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"part_offsets_subscribe\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, PartOffsetsSubscribePacket> CODEC = getCodec(PartOffsetsSubscribePacket::new);\n\n    @CodecField\n    private boolean subscribe;\n\n    public PartOffsetsSubscribePacket() {\n        super(ID);\n    }\n\n    public PartOffsetsSubscribePacket(boolean subscribe) {\n        super(ID);\n        this.subscribe = subscribe;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        if (subscribe) {\n            PartOffsetsClientNotifier.getInstance().registerPlayer(player);\n        } else {\n            PartOffsetsClientNotifier.getInstance().unRegisterPlayer(player);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/PlayerTeleportPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.portal.TeleportTransition;\nimport net.minecraft.world.phys.Vec3;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.Reference;\n\n/**\n * Packet for subscribing a network update to a player.\n * @author rubensworks\n *\n */\npublic class PlayerTeleportPacket extends PacketCodec {\n\n    public static final Type<PlayerTeleportPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"player_teleport\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, PlayerTeleportPacket> CODEC = getCodec(PlayerTeleportPacket::new);\n\n    @CodecField\n    private String dimension;\n    @CodecField\n    private double x;\n    @CodecField\n    private double y;\n    @CodecField\n    private double z;\n    @CodecField\n    private float yaw;\n    @CodecField\n    private float pitch;\n\n    public PlayerTeleportPacket() {\n        super(ID);\n    }\n\n    public PlayerTeleportPacket(ResourceKey<Level> dimension, double x, double y, double z, float yaw, float pitch) {\n        super(ID);\n        this.dimension = dimension.identifier().toString();\n        this.x = x;\n        this.y = y;\n        this.z = z;\n        this.yaw = yaw;\n        this.pitch = pitch;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n        ResourceKey<Level> dimensionType = ResourceKey.create(Registries.DIMENSION, Identifier.parse(this.dimension));\n        if (!dimensionType.identifier().equals(player.level().dimension().identifier())) {\n            player.teleport(new TeleportTransition(\n                    ServerLifecycleHooks.getCurrentServer().getLevel(dimensionType),\n                    new Vec3(x, y, z),\n                    Vec3.ZERO, yaw, pitch,\n                    TeleportTransition.DO_NOTHING\n            ));\n        }\n        player.connection.teleport(x + 0.5F, y + 0.5F, z + 0.5F, yaw, pitch);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/network/packet/SpeakTextPacket.java",
    "content": "package org.cyclops.integrateddynamics.network.packet;\n\nimport com.mojang.text2speech.Narrator;\nimport net.minecraft.network.RegistryFriendlyByteBuf;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.network.CodecField;\nimport org.cyclops.cyclopscore.network.PacketCodec;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.Reference;\n\nimport java.util.Date;\n\n/**\n * Packet for speaking text.\n *\n * @author rubensworks\n */\npublic class SpeakTextPacket extends PacketCodec {\n\n    public static final Type<SpeakTextPacket> ID = new Type<>(Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"speak_text\"));\n    public static final StreamCodec<RegistryFriendlyByteBuf, SpeakTextPacket> CODEC = getCodec(SpeakTextPacket::new);\n\n    public static long lastSay = 0;\n\n    @CodecField\n    private String text;\n    @CodecField\n    private float volume;\n\n    public SpeakTextPacket() {\n        super(ID);\n    }\n\n    public SpeakTextPacket(String text, float volume) {\n        super(ID);\n        this.text = text;\n        this.volume = volume;\n    }\n\n    @Override\n    public boolean isAsync() {\n        return false;\n    }\n\n    @Override\n    public void actionClient(Level world, Player player) {\n        if (new Date().getTime() >= lastSay + GeneralConfig.speachMaxFrequency) {\n            Narrator.getNarrator().clear();\n            Narrator.getNarrator().say(this.text, true, this.volume);\n            lastSay = new Date().getTime();\n        }\n    }\n\n    @Override\n    public void actionServer(Level world, ServerPlayer player) {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeAudioReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An audio reader part.\n * @author rubensworks\n */\npublic class PartTypeAudioReader extends PartTypeReadBase<PartTypeAudioReader, PartStateReaderBase<PartTypeAudioReader>> {\n\n    public PartTypeAudioReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Audio.INTEGER_HARP_NOTE,\n                Aspects.Read.Audio.INTEGER_BASEDRUM_NOTE,\n                Aspects.Read.Audio.INTEGER_SNARE_NOTE,\n                Aspects.Read.Audio.INTEGER_HAT_NOTE,\n                Aspects.Read.Audio.INTEGER_BASS_NOTE,\n                Aspects.Read.Audio.INTEGER_FLUTE_NOTE,\n                Aspects.Read.Audio.INTEGER_BELL_NOTE,\n                Aspects.Read.Audio.INTEGER_GUITAR_NOTE,\n                Aspects.Read.Audio.INTEGER_CHIME_NOTE,\n                Aspects.Read.Audio.INTEGER_XYLOPHONE_NOTE,\n                Aspects.Read.Audio.INTEGER_IRON_XYLOPHONE_NOTE,\n                Aspects.Read.Audio.INTEGER_COW_BELL_NOTE,\n                Aspects.Read.Audio.INTEGER_DIDGERIDOO_NOTE,\n                Aspects.Read.Audio.INTEGER_BIT_NOTE,\n                Aspects.Read.Audio.INTEGER_BANJO_NOTE,\n                Aspects.Read.Audio.INTEGER_PLING_NOTE,\n                Aspects.Read.Audio.INTEGER_ZOMBIE_NOTE,\n                Aspects.Read.Audio.INTEGER_SKELETON_NOTE,\n                Aspects.Read.Audio.INTEGER_CREEPER_NOTE,\n                Aspects.Read.Audio.INTEGER_DRAGON_NOTE,\n                Aspects.Read.Audio.INTEGER_WITHER_SKELETON_NOTE,\n                Aspects.Read.Audio.INTEGER_PIGLIN_NOTE,\n                Aspects.Read.Audio.INTEGER_CUSTOM_HEAD_NOTE\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeAudioReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeAudioReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeAudioReader> state) {\n        return GeneralConfig.audioReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeAudioWriter.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.write.PartStateWriterBase;\nimport org.cyclops.integrateddynamics.core.part.write.PartTypeWriteBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An audio writer part.\n * @author rubensworks\n */\npublic class PartTypeAudioWriter extends PartTypeWriteBase<PartTypeAudioWriter, PartStateWriterBase<PartTypeAudioWriter>> {\n\n    public PartTypeAudioWriter(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Write.Audio.INTEGER_HARP_NOTE,\n                Aspects.Write.Audio.INTEGER_BASEDRUM_NOTE,\n                Aspects.Write.Audio.INTEGER_SNARE_NOTE,\n                Aspects.Write.Audio.INTEGER_HAT_NOTE,\n                Aspects.Write.Audio.INTEGER_BASS_NOTE,\n                Aspects.Write.Audio.INTEGER_FLUTE_NOTE,\n                Aspects.Write.Audio.INTEGER_BELL_NOTE,\n                Aspects.Write.Audio.INTEGER_GUITAR_NOTE,\n                Aspects.Write.Audio.INTEGER_CHIME_NOTE,\n                Aspects.Write.Audio.INTEGER_XYLOPHONE_NOTE,\n                Aspects.Write.Audio.INTEGER_IRON_XYLOPHONE_NOTE,\n                Aspects.Write.Audio.INTEGER_COW_BELL_NOTE,\n                Aspects.Write.Audio.INTEGER_DIDGERIDOO_NOTE,\n                Aspects.Write.Audio.INTEGER_BIT_NOTE,\n                Aspects.Write.Audio.INTEGER_BANJO_NOTE,\n                Aspects.Write.Audio.INTEGER_PLING_NOTE,\n                Aspects.Write.Audio.INTEGER_ZOMBIE_NOTE,\n                Aspects.Write.Audio.INTEGER_SKELETON_NOTE,\n                Aspects.Write.Audio.INTEGER_CREEPER_NOTE,\n                Aspects.Write.Audio.INTEGER_DRAGON_NOTE,\n                Aspects.Write.Audio.INTEGER_WITHER_SKELETON_NOTE,\n                Aspects.Write.Audio.INTEGER_PIGLIN_NOTE,\n                Aspects.Write.Audio.INTEGER_CUSTOM_HEAD_NOTE,\n                Aspects.Write.Audio.STRING_SOUND,\n                Aspects.Write.Audio.STRING_TEXT\n        ));\n    }\n\n    @Override\n    public PartStateWriterBase<PartTypeAudioWriter> constructDefaultState() {\n        return new PartStateWriterBase<PartTypeAudioWriter>(Aspects.REGISTRY.getAspects(this).size());\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateWriterBase<PartTypeAudioWriter> state) {\n        return GeneralConfig.audioWriterBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeBlockReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An block reader part.\n * @author rubensworks\n */\npublic class PartTypeBlockReader extends PartTypeReadBase<PartTypeBlockReader, PartStateReaderBase<PartTypeBlockReader>> {\n\n    public PartTypeBlockReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Block.BOOLEAN_BLOCK,\n                Aspects.Read.Block.STRING_DIMENSION,\n                Aspects.Read.Block.INTEGER_POSX,\n                Aspects.Read.Block.INTEGER_POSY,\n                Aspects.Read.Block.INTEGER_POSZ,\n                Aspects.Read.Block.BLOCK,\n                Aspects.Read.Block.NBT,\n                Aspects.Read.Block.STRING_BIOME,\n                Aspects.Read.Block.INTEGER_LIGHT\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeBlockReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeBlockReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeBlockReader> state) {\n        return GeneralConfig.blockReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeConnector.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartCapability;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.part.PartStateBase;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\n\nimport java.util.Optional;\n\n/**\n * A base wireless connector part.\n * @author rubensworks\n */\npublic abstract class PartTypeConnector<P extends PartTypeConnector<P, S>, S extends PartTypeConnector.State<P>> extends PartTypeBase<P, S> {\n\n    public PartTypeConnector(String name, PartRenderPosition partRenderPosition) {\n        super(name, partRenderPosition);\n    }\n\n    @Override\n    public boolean supportsOffsets() {\n        return false;\n    }\n\n    @Override\n    public boolean isUpdate(S state) {\n        return getConsumptionRate(state) > 0 || super.isUpdate(state);\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlockStatus(properties);\n    }\n\n    @Override\n    public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        super.afterNetworkReAlive(network, partNetwork, target, state);\n        state.setPosition(target.getCenter());\n    }\n\n    @Override\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {\n        super.onNetworkAddition(network, partNetwork, target, state);\n        state.setPosition(target.getCenter());\n    }\n\n    public static abstract class State<P extends PartTypeConnector> extends PartStateBase<P> implements IPathElement {\n\n        private PartPos partPos;\n\n        protected PartPos getPartPos() {\n            return partPos;\n        }\n\n        @Override\n        public DimPos getPosition() {\n            return this.partPos == null ? null : this.partPos.getPos();\n        }\n\n        public void setPosition(PartPos partPos) {\n            this.partPos = partPos;\n        }\n\n        @Override\n        public int compareTo(IPathElement o) {\n            return getPosition().compareTo(o.getPosition());\n        }\n\n        @Override\n        public <T> Optional<T> getCapability(P partType, PartCapability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {\n            if (capability == Capabilities.PathElement.PART) {\n                return Optional.of((T) this);\n            }\n            return super.getCapability(partType, capability, network, partNetwork, target);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeConnectorMonoDirectional.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.particles.DustParticleOptions;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.util.ProblemReporter;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.path.IPathElement;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\nimport org.cyclops.integrateddynamics.capability.path.SidedPathElement;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlock;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\n\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * A monodirectional wireless connector part that can connect to\n * at most one other monodirectional connector in a straight line.\n * @author rubensworks\n */\npublic class PartTypeConnectorMonoDirectional extends PartTypeConnector<PartTypeConnectorMonoDirectional, PartTypeConnectorMonoDirectional.State> {\n\n    public PartTypeConnectorMonoDirectional(String name) {\n        super(name, new PartRenderPosition(0.25F, 0.3125F, 0.5F, 0.5F));\n    }\n\n    @Override\n    public int getConsumptionRate(State state) {\n        return GeneralConfig.connectorMonoDirectionalBaseConsumption;\n    }\n\n    @Override\n    public PartTypeConnectorMonoDirectional.State constructDefaultState() {\n        return new PartTypeConnectorMonoDirectional.State();\n    }\n\n    @Override\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {\n        super.onNetworkAddition(network, partNetwork, target, state);\n\n        // Find and link two parts\n        if (!state.hasTarget()) {\n            int offset = findTargetOffset(target.getCenter());\n            if (offset > 0) {\n                state.setTarget(offset);\n                state.getTargetState(target.getCenter()).setTarget(offset);\n\n                // Re-init network at the two disconnected connectors\n                DimPos originPos = target.getCenter().getPos();\n                DimPos targetPos = PartTypeConnectorMonoDirectional.State.getTargetPos(target.getCenter(), state.getOffset());\n                NetworkHelpers.initNetwork(originPos.getLevel(true), originPos.getBlockPos(), target.getCenter().getSide());\n                NetworkHelpers.initNetwork(targetPos.getLevel(true), targetPos.getBlockPos(), target.getCenter().getSide().getOpposite());\n            }\n        }\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {\n        super.onPostRemoved(network, partNetwork, target, state);\n\n        if (state.hasTarget()) {\n            // Remove target information in both linked parts\n            PartTypeConnectorMonoDirectional.State targetState = state.getTargetState(target.getCenter());\n            DimPos originPos = target.getCenter().getPos();\n            DimPos targetPos = null;\n            if (targetState != null) {\n                targetState.removeTarget();\n                targetPos = PartTypeConnectorMonoDirectional.State.getTargetPos(target.getCenter(), state.getOffset());\n            }\n            state.removeTarget();\n\n            // Re-init network at the two disconnected connectors\n            NetworkHelpers.initNetwork(originPos.getLevel(true), originPos.getBlockPos(),  target.getCenter().getSide());\n            if (targetPos != null) {\n                NetworkHelpers.initNetwork(targetPos.getLevel(true), targetPos.getBlockPos(),  target.getCenter().getSide().getOpposite());\n            }\n        }\n    }\n\n    @Override\n    public ItemStack getItemStack(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, State state, boolean saveState) {\n        // Set offset to 0 to make sure it is not stored in the item\n        int offset = state.getOffset();\n        state.setOffset(0);\n\n        // Serialize to item\n        ItemStack itemStack = super.getItemStack(valueDeseralizationContext, problemPath, state, saveState);\n\n        // Set original offset back\n        state.setOffset(offset);\n\n        return itemStack;\n    }\n\n    /**\n     * Look in the part's direction for an unbound monodirectional connector.\n     * @param origin The origin position to start looking from.\n     * @return The other connector's distance, or 0 if not found.\n     */\n    protected int findTargetOffset(PartPos origin) {\n        int offset = 0;\n        PartTypeConnectorMonoDirectional.State state = null;\n        while (++offset < GeneralConfig.maxDirectionalConnectorOffset\n                && (state = PartTypeConnectorMonoDirectional.State.getUnboundTargetState(origin, offset)) == null);\n        if (state != null) {\n            return offset;\n        }\n        return 0;\n    }\n\n    protected IgnoredBlockStatus.Status getStatus(PartTypeConnectorMonoDirectional.State state) {\n        return state != null && state.hasTarget()\n                ? IgnoredBlockStatus.Status.ACTIVE : IgnoredBlockStatus.Status.INACTIVE;\n    }\n\n    @Override\n    public BlockState getBlockState(IPartContainer partContainer, Direction side) {\n        IgnoredBlockStatus.Status status = getStatus(partContainer != null\n                ? (PartTypeConnectorMonoDirectional.State) partContainer.getPartState(side) : null);\n        return super.getBlockState(partContainer, side)\n                .setValue(IgnoredBlock.FACING, side)\n                .setValue(IgnoredBlockStatus.STATUS, status);\n    }\n\n    public static class State extends PartTypeConnector.State<PartTypeConnectorMonoDirectional> {\n\n        private int offset = 0;\n\n        @Override\n        public Set<ISidedPathElement> getReachableElements() {\n            if (getPartPos() != null) {\n                Direction targetSide = getPartPos().getSide().getOpposite();\n                IPathElement pathElement = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(State.getTargetPos(getPartPos(), offset),\n                        targetSide, Capabilities.PathElement.BLOCK).orElse(null);\n                if (pathElement != null) {\n                    return Sets.newHashSet(SidedPathElement.of(pathElement, targetSide));\n                }\n            }\n            return Collections.emptySet();\n        }\n\n        public void setTarget(int offset) {\n            setOffset(offset);\n            sendUpdate();\n\n            DimPos dimPos = getPosition();\n            if (dimPos != null && this.offset > 0) {\n                BlockPos pos = dimPos.getBlockPos();\n                for (int i = 1; i < this.offset; i++) {\n                    pos = pos.relative(getPartPos().getSide());\n                    ((ServerLevel) getPosition().getLevel(true))\n                            .sendParticles(DustParticleOptions.REDSTONE,\n                                    pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1, 0D, 0D, 0D, 0);\n                }\n            }\n        }\n\n        public boolean hasTarget() {\n            return this.offset > 0;\n        }\n\n        public int getOffset() {\n            return this.offset;\n        }\n\n        /**\n         * Set the raw offset.\n         * Prefer {@link #setTarget(int)}.\n         * @param offset The new offset.\n         */\n        public void setOffset(int offset) {\n            this.offset = offset;\n        }\n\n        public void removeTarget() {\n            setTarget(0);\n        }\n\n        protected PartTypeConnectorMonoDirectional.State getTargetState(PartPos origin) {\n            return getTargetState(origin, offset);\n        }\n\n        @Override\n        public void serialize(ValueOutput valueOutput) {\n            super.serialize(valueOutput);\n            if (offset > 0) {\n                valueOutput.putInt(\"connect_offset\", offset);\n            }\n        }\n\n        @Override\n        public void deserialize(ValueInput valueInput) {\n            super.deserialize(valueInput);\n            this.offset = valueInput.getIntOr(\"connect_offset\", 0);\n        }\n\n        protected static PartTypeConnectorMonoDirectional.State getUnboundTargetState(PartPos origin, int offset) {\n            PartTypeConnectorMonoDirectional.State state = getTargetState(origin, offset);\n            if (state != null && !state.hasTarget()) {\n                return state;\n            }\n            return null;\n        }\n\n        protected static PartTypeConnectorMonoDirectional.State getTargetState(PartPos origin, int offset) {\n            PartPos targetPos = PartPos.of(getTargetPos(origin, offset), origin.getSide().getOpposite());\n            PartHelpers.PartStateHolder partStateHolder = PartHelpers.getPart(targetPos);\n            if (partStateHolder != null && partStateHolder.getPart() instanceof PartTypeConnectorMonoDirectional) {\n                return (State) partStateHolder.getState();\n            }\n            return null;\n        }\n\n        protected static DimPos getTargetPos(PartPos origin, int offset) {\n            return DimPos.of(origin.getPos().getLevelKey(),\n                    origin.getPos().getBlockPos().relative(origin.getSide(), offset));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeConnectorOmniDirectional.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Sets;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.ProblemReporter;\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.crafting.CraftingInput;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.neoforged.neoforge.event.server.ServerStartedEvent;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartContainer;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartRenderPosition;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.path.ISidedPathElement;\nimport org.cyclops.integrateddynamics.capability.path.SidedPathElement;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlock;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\n/**\n * An omnidirectional wireless connector part that can connect to\n * all other monodirectional connectors of the same group anywhere in any dimension.\n * @author rubensworks\n */\npublic class PartTypeConnectorOmniDirectional extends PartTypeConnector<PartTypeConnectorOmniDirectional, PartTypeConnectorOmniDirectional.State> {\n\n    public static LoadedGroups LOADED_GROUPS = new LoadedGroups();\n\n    public PartTypeConnectorOmniDirectional(String name) {\n        super(name, new PartRenderPosition(0.25F, 0.3125F, 0.625F, 0.625F));\n    }\n\n    @Override\n    public int getConsumptionRate(State state) {\n        return GeneralConfig.connectorOmniDirectionalBaseConsumption;\n    }\n\n    @Override\n    public PartTypeConnectorOmniDirectional.State constructDefaultState() {\n        return new PartTypeConnectorOmniDirectional.State();\n    }\n\n    @Override\n    public ItemStack getItemStack(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, State state, boolean saveState) {\n        ItemStack itemStack = super.getItemStack(valueDeseralizationContext, problemPath, state, saveState);\n        if (state.hasConnectorId()) {\n            itemStack.set(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP, state.getGroupId());\n        }\n        return itemStack;\n    }\n\n    @Override\n    public State getState(ValueDeseralizationContext valueDeseralizationContext, ProblemReporter.PathElement problemPath, ItemStack itemStack) {\n        State state = super.getState(valueDeseralizationContext, problemPath, itemStack);\n        if (itemStack.has(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP)) {\n            state.setGroupId(itemStack.get(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP));\n        } else {\n            state.setGroupId(PartTypeConnectorOmniDirectional.generateGroupId());\n        }\n        return state;\n    }\n\n    @Override\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {\n        super.onNetworkAddition(network, partNetwork, target, state);\n        addPosition(network, state, target.getCenter());\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {\n        super.onPostRemoved(network, partNetwork, target, state);\n        removePosition(network, state, target.getCenter());\n    }\n\n    protected void addPosition(INetwork network, State state, PartPos pos) {\n        if (!PartTypeConnectorOmniDirectional.LOADED_GROUPS.isModifyingPositions()\n                && !state.isAddedToGroup()) {\n            state.setAddedToGroup(true);\n            PartTypeConnectorOmniDirectional.LOADED_GROUPS.addPosition(state.getGroupId(), pos, network.isInitialized());\n        }\n    }\n\n    protected void removePosition(INetwork network, State state, PartPos pos) {\n        if (!PartTypeConnectorOmniDirectional.LOADED_GROUPS.isModifyingPositions()\n                && state.isAddedToGroup()) {\n            if (state.hasConnectorId()) {\n                state.setAddedToGroup(false);\n                PartTypeConnectorOmniDirectional.LOADED_GROUPS.removePosition(state.getGroupId(), pos, network.isInitialized());\n            }\n        }\n    }\n\n    public static int generateGroupId() {\n        return IntegratedDynamics.globalCounters.get().getNext(\"omnidir-connectors\");\n    }\n\n    @Override\n    public void loadTooltip(State state, List<Component> lines) {\n        super.loadTooltip(state, lines);\n        lines.add(Component.translatable(L10NValues.PART_TOOLTIP_MONODIRECTIONALCONNECTOR_GROUP, state.getGroupId()));\n    }\n\n    @Override\n    public void loadTooltip(ItemStack itemStack, Consumer<Component> tooltipAdder) {\n        super.loadTooltip(itemStack, tooltipAdder);\n        if (itemStack.has(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP)) {\n            tooltipAdder.accept(Component.translatable(L10NValues.PART_TOOLTIP_MONODIRECTIONALCONNECTOR_GROUP,\n                    itemStack.get(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP)));\n        }\n    }\n\n    protected IgnoredBlockStatus.Status getStatus(PartTypeConnectorOmniDirectional.State state) {\n        return state != null && state.hasConnectorId()\n                ? IgnoredBlockStatus.Status.ACTIVE : IgnoredBlockStatus.Status.INACTIVE;\n    }\n\n    @Override\n    public BlockState getBlockState(IPartContainer partContainer, Direction side) {\n        IgnoredBlockStatus.Status status = getStatus(partContainer != null\n                ? (PartTypeConnectorOmniDirectional.State) partContainer.getPartState(side) : null);\n        return super.getBlockState(partContainer, side)\n                .setValue(IgnoredBlock.FACING, side)\n                .setValue(IgnoredBlockStatus.STATUS, status);\n    }\n\n    public static ItemStack transformCraftingOutput(CraftingInput inventory, ItemStack staticOutput) {\n        // When crafting the item, either copy the group id from the existing item or generate a new id.\n        int groupId = -1, stackCount = 0;\n        for (int i = 0; i < inventory.size(); i++) {\n            ItemStack slotStack = inventory.getItem(i);\n            if (!slotStack.isEmpty()) {\n                ++stackCount;\n                if(groupId == -1 && slotStack.getItem() == PartTypes.CONNECTOR_OMNI.getItem() && slotStack.has(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP)) {\n                    groupId = slotStack.get(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP);\n                }\n            }\n        }\n        if(stackCount == 1) {\n            groupId = -1; // If we're resetting a connector, give it a new ID\n        }\n\n        if (groupId < 0) {\n            groupId = generateGroupId();\n        }\n\n        staticOutput = staticOutput.copy();\n        staticOutput.set(RegistryEntries.DATACOMPONENT_OMNIDIRECTIONAL_GROUP, groupId);\n\n        return staticOutput;\n    }\n\n    @Override\n    public InteractionResult onPartActivated(State partState, BlockPos pos, Level world, Player player, InteractionHand hand, ItemStack heldItem, BlockHitResult hit) {\n        // Drop through if the player is sneaking\n        if(player.isSecondaryUseActive() || !partState.isEnabled()) {\n            return InteractionResult.PASS;\n        }\n        if (world.isClientSide()) {\n            player.sendSystemMessage(Component.translatable(L10NValues.PART_TOOLTIP_MONODIRECTIONALCONNECTOR_GROUP,\n                    partState.getGroupId()));\n        }\n\n        return InteractionResult.SUCCESS;\n    }\n\n    public static class State extends PartTypeConnector.State<PartTypeConnectorOmniDirectional> {\n\n        private int groupId = -1;\n        private boolean addedToGroup = false;\n\n        @Override\n        public void serialize(ValueOutput valueOutput) {\n            super.serialize(valueOutput);\n            valueOutput.putInt(\"groupId\", groupId);\n        }\n\n        @Override\n        public void deserialize(ValueInput valueInput) {\n            super.deserialize(valueInput);\n            this.groupId = valueInput.getIntOr(\"groupId\", -1);\n        }\n\n        @Override\n        public Set<ISidedPathElement> getReachableElements() {\n            if (hasConnectorId()) {\n                Set<ISidedPathElement> pathElements = Sets.newTreeSet();\n                for (PartPos pos : PartTypeConnectorOmniDirectional.LOADED_GROUPS.getPositions(getGroupId())) {\n                    if (!pos.equals(this.getPartPos())) {\n                        IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(pos.getPos(), pos.getSide(), Capabilities.PathElement.BLOCK)\n                                .ifPresent(pathElement -> pathElements.add(SidedPathElement.of(pathElement, pos.getSide())));\n                    }\n                }\n                return pathElements;\n            }\n            return Collections.emptySet();\n        }\n\n        public int getGroupId() {\n            return groupId;\n        }\n\n        public void setGroupId(int groupId) {\n            this.groupId = groupId;\n            sendUpdate();\n        }\n\n        public boolean hasConnectorId() {\n            return this.groupId >= 0;\n        }\n\n        public boolean isAddedToGroup() {\n            return addedToGroup;\n        }\n\n        public void setAddedToGroup(boolean addedToGroup) {\n            this.addedToGroup = addedToGroup;\n        }\n    }\n\n    public static class LoadedGroups {\n\n        private Int2ObjectMap<Set<PartPos>> groupPositions = new Int2ObjectOpenHashMap<>();\n        private boolean modifyingPositions = false;\n\n        public void onStartedEvent(ServerStartedEvent event) {\n            // Reset to avoid ghost-groups on world-change.\n            groupPositions.clear();\n        }\n\n        public Set<PartPos> getPositions(int group) {\n            Set<PartPos> positions = groupPositions.get(group);\n            return positions != null ? positions : Collections.<PartPos>emptySet();\n        }\n\n        protected void initNetworkGroup(Set<PartPos> positions) {\n            for (PartPos position : positions) {\n                if (position.getPos().isLoaded()) {\n                    NetworkHelpers.initNetwork(position.getPos().getLevel(true), position.getPos().getBlockPos(), position.getSide());\n                }\n            }\n        }\n\n        public void addPosition(int group, PartPos pos, boolean initNetwork) {\n            Set<PartPos> positions = groupPositions.get(group);\n            if (positions == null) {\n                groupPositions.put(group, positions = Sets.newTreeSet());\n            }\n            positions.add(pos);\n\n            if (initNetwork) {\n                modifyingPositions = true;\n                initNetworkGroup(positions);\n                modifyingPositions = false;\n            }\n        }\n\n        public void removePosition(int group, PartPos pos, boolean initNetwork) {\n            Set<PartPos> positions = groupPositions.get(group);\n            if (positions == null) {\n                groupPositions.put(group, positions = Sets.newTreeSet());\n            }\n            positions.remove(pos);\n\n            if (initNetwork) {\n                modifyingPositions = true;\n                initNetworkGroup(positions);\n                if (pos.getPos().isLoaded()) {\n                    NetworkHelpers.initNetwork(pos.getPos().getLevel(true), pos.getPos().getBlockPos(), pos.getSide());\n                }\n                modifyingPositions = false;\n            }\n        }\n\n        public boolean isModifyingPositions() {\n            return modifyingPositions;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeEffectWriter.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.core.particles.ParticleTypes;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.write.PartStateWriterBase;\nimport org.cyclops.integrateddynamics.core.part.write.PartTypeWriteBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An effect writer part.\n * @author rubensworks\n */\npublic class PartTypeEffectWriter extends PartTypeWriteBase<PartTypeEffectWriter, PartStateWriterBase<PartTypeEffectWriter>> {\n\n    public PartTypeEffectWriter(String name) {\n        super(name);\n        Aspects.REGISTRY.register(this, Lists.<IAspect>newArrayList(\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ANGRY_VILLAGER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.BUBBLE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.CLOUD),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.CRIT),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DAMAGE_INDICATOR),\n                Aspects.Write.Effect.createForParticlePower(ParticleTypes.DRAGON_BREATH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DRIPPING_LAVA),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_LAVA),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.LANDING_LAVA),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DRIPPING_WATER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_WATER),\n                Aspects.Write.Effect.createForParticleSpell(ParticleTypes.EFFECT),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ELDER_GUARDIAN),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ENCHANTED_HIT),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ENCHANT),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.END_ROD),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.EXPLOSION_EMITTER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.EXPLOSION),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SONIC_BOOM),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FIREWORK),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FISHING),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FLAME),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SCULK_SOUL),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SCULK_CHARGE_POP),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SOUL_FIRE_FLAME),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SOUL),\n                Aspects.Write.Effect.createForParticleColor(ParticleTypes.FLASH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.HAPPY_VILLAGER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.COMPOSTER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.HEART),\n                Aspects.Write.Effect.createForParticleSpell(ParticleTypes.INSTANT_EFFECT),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ITEM_SLIME),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ITEM_SNOWBALL),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.LARGE_SMOKE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.LAVA),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.MYCELIUM),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.NOTE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.POOF),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.PORTAL),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.RAIN),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SMOKE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SNEEZE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SPIT),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SQUID_INK),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SWEEP_ATTACK),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.TOTEM_OF_UNDYING),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.UNDERWATER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SPLASH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.WITCH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.BUBBLE_POP),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.CURRENT_DOWN),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.BUBBLE_COLUMN_UP),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.NAUTILUS),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DOLPHIN),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.CAMPFIRE_COSY_SMOKE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DRIPPING_HONEY),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_HONEY),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.LANDING_HONEY),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_NECTAR),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_SPORE_BLOSSOM),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ASH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.CRIMSON_SPORE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.WARPED_SPORE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SPORE_BLOSSOM_AIR),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DRIPPING_OBSIDIAN_TEAR),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_OBSIDIAN_TEAR),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.LANDING_OBSIDIAN_TEAR),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.REVERSE_PORTAL),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.WHITE_ASH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SMALL_FLAME),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SNOWFLAKE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DRIPPING_DRIPSTONE_LAVA),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_DRIPSTONE_LAVA),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DRIPPING_DRIPSTONE_WATER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FALLING_DRIPSTONE_WATER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.GLOW_SQUID_INK),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.GLOW),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.WAX_ON),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.WAX_OFF),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.ELECTRIC_SPARK),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.SCRAPE),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.EGG_CRACK),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.DUST_PLUME),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.TRIAL_SPAWNER_DETECTED_PLAYER),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.TRIAL_SPAWNER_DETECTED_PLAYER_OMINOUS),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.VAULT_CONNECTION),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.OMINOUS_SPAWNING),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.RAID_OMEN),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.TRIAL_OMEN),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.FIREFLY),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.PAUSE_MOB_GROWTH),\n                Aspects.Write.Effect.createForParticle(ParticleTypes.RESET_MOB_GROWTH)\n        ));\n    }\n\n    @Override\n    public PartStateWriterBase<PartTypeEffectWriter> constructDefaultState() {\n        return new PartStateWriterBase<PartTypeEffectWriter>(Aspects.REGISTRY.getAspects(this).size());\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateWriterBase<PartTypeEffectWriter> state) {\n        return GeneralConfig.effectWriterBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeEntityReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An entity reader part.\n * @author rubensworks\n */\npublic class PartTypeEntityReader extends PartTypeReadBase<PartTypeEntityReader, PartStateReaderBase<PartTypeEntityReader>> {\n\n    public PartTypeEntityReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Entity.INTEGER_ITEMFRAMEROTATION,\n                Aspects.Read.Entity.LIST_ENTITIES,\n                Aspects.Read.Entity.LIST_PLAYERS,\n                Aspects.Read.Entity.ENTITY,\n                Aspects.Read.Entity.ITEMSTACK_ITEMFRAMECONTENTS\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeEntityReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeEntityReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeEntityReader> state) {\n        return GeneralConfig.entityReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeEntityWriter.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.write.PartStateWriterBase;\nimport org.cyclops.integrateddynamics.core.part.write.PartTypeWriteBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An entity writer part.\n * @author josephcsible\n */\npublic class PartTypeEntityWriter extends PartTypeWriteBase<PartTypeEntityWriter, PartStateWriterBase<PartTypeEntityWriter>> {\n\n    public PartTypeEntityWriter(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n\n        ));\n    }\n\n    @Override\n    public PartStateWriterBase<PartTypeEntityWriter> constructDefaultState() {\n        return new PartStateWriterBase<PartTypeEntityWriter>(Aspects.REGISTRY.getAspects(this).size());\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateWriterBase<PartTypeEntityWriter> state) {\n        return GeneralConfig.entityWriterBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeExtraDimensionalReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An extra-dimensional property reader part.\n * @author rubensworks\n */\npublic class PartTypeExtraDimensionalReader extends PartTypeReadBase<PartTypeExtraDimensionalReader, PartStateReaderBase<PartTypeExtraDimensionalReader>> {\n\n    public PartTypeExtraDimensionalReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.ExtraDimensional.INTEGER_RANDOM,\n                Aspects.Read.ExtraDimensional.INTEGER_PLAYERCOUNT,\n                Aspects.Read.ExtraDimensional.INTEGER_TICKTIME,\n                Aspects.Read.ExtraDimensional.DOUBLE_TPS,\n                Aspects.Read.ExtraDimensional.LIST_PLAYERS\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeExtraDimensionalReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeExtraDimensionalReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeExtraDimensionalReader> state) {\n        return GeneralConfig.extraDimensionalReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeFluidReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An fluid reader part.\n * @author rubensworks\n */\npublic class PartTypeFluidReader extends PartTypeReadBase<PartTypeFluidReader, PartStateReaderBase<PartTypeFluidReader>> {\n\n    public PartTypeFluidReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Fluid.BOOLEAN_FULL,\n                Aspects.Read.Fluid.BOOLEAN_EMPTY,\n                Aspects.Read.Fluid.BOOLEAN_NONEMPTY,\n                Aspects.Read.Fluid.BOOLEAN_APPLICABLE,\n                Aspects.Read.Fluid.LONG_AMOUNT,\n                Aspects.Read.Fluid.LONG_AMOUNTTOTAL,\n                Aspects.Read.Fluid.LONG_CAPACITY,\n                Aspects.Read.Fluid.LONG_CAPACITYTOTAL,\n                Aspects.Read.Fluid.INTEGER_TANKS,\n                Aspects.Read.Fluid.DOUBLE_FILLRATIO,\n                Aspects.Read.Fluid.LIST_TANKFLUIDS,\n                Aspects.Read.Fluid.LIST_TANKCAPACITIES,\n                Aspects.Read.Fluid.FLUIDSTACK,\n                Aspects.Read.Fluid.BLOCK\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeFluidReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeFluidReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeFluidReader> state) {\n        return GeneralConfig.fluidReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeInventoryReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An inventory reader part.\n * @author rubensworks\n */\npublic class PartTypeInventoryReader extends PartTypeReadBase<PartTypeInventoryReader, PartStateReaderBase<PartTypeInventoryReader>> {\n\n    public PartTypeInventoryReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Inventory.BOOLEAN_FULL,\n                Aspects.Read.Inventory.BOOLEAN_EMPTY,\n                Aspects.Read.Inventory.BOOLEAN_NONEMPTY,\n                Aspects.Read.Inventory.BOOLEAN_APPLICABLE,\n                Aspects.Read.Inventory.INTEGER_COUNT,\n                Aspects.Read.Inventory.INTEGER_SLOTS,\n                Aspects.Read.Inventory.INTEGER_SLOTSFILLED,\n                Aspects.Read.Inventory.DOUBLE_FILLRATIO,\n                Aspects.Read.Inventory.LIST_ITEMSTACKS,\n                Aspects.Read.Inventory.OBJECT_ITEM_STACK_SLOT\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeInventoryReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeInventoryReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeInventoryReader> state) {\n        return GeneralConfig.inventoryReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeInventoryWriter.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.write.PartStateWriterBase;\nimport org.cyclops.integrateddynamics.core.part.write.PartTypeWriteBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An inventory writer part.\n * @author rubensworks\n */\npublic class PartTypeInventoryWriter extends PartTypeWriteBase<PartTypeInventoryWriter, PartStateWriterBase<PartTypeInventoryWriter>> {\n\n    public PartTypeInventoryWriter(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n\n        ));\n    }\n\n    @Override\n    public PartStateWriterBase<PartTypeInventoryWriter> constructDefaultState() {\n        return new PartStateWriterBase<PartTypeInventoryWriter>(Aspects.REGISTRY.getAspects(this).size());\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateWriterBase<PartTypeInventoryWriter> state) {\n        return GeneralConfig.inventoryWriterBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeMachineReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * A machine reader part.\n * @author rubensworks\n */\npublic class PartTypeMachineReader extends PartTypeReadBase<PartTypeMachineReader, PartStateReaderBase<PartTypeMachineReader>> {\n\n    public PartTypeMachineReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Machine.BOOLEAN_ISWORKER,\n                Aspects.Read.Machine.BOOLEAN_HASWORK,\n                Aspects.Read.Machine.BOOLEAN_CANWORK,\n                Aspects.Read.Machine.BOOLEAN_ISWORKING,\n                Aspects.Read.Machine.BOOLEAN_ISTEMPERATURE,\n                Aspects.Read.Machine.DOUBLE_TEMPERATURE,\n                Aspects.Read.Machine.DOUBLE_MAXTEMPERATURE,\n                Aspects.Read.Machine.DOUBLE_MINTEMPERATURE,\n                Aspects.Read.Machine.DOUBLE_DEFAULTTEMPERATURE,\n                Aspects.Read.Machine.BOOLEAN_ISRECIPEHANDLER,\n                Aspects.Read.Machine.LIST_GETRECIPES,\n                Aspects.Read.Machine.OPERATOR_GETRECIPEBYINPUT,\n                Aspects.Read.Machine.OPERATOR_GETRECIPEBYOUTPUT,\n                Aspects.Read.Machine.OPERATOR_GETRECIPESBYINPUT,\n                Aspects.Read.Machine.OPERATOR_GETRECIPESBYOUTPUT,\n                Aspects.Read.Machine.OPERATOR_GETRECIPEOUTPUT,\n                Aspects.Read.Machine.OPERATOR_GETRECIPEINPUTS,\n                Aspects.Read.Machine.BOOLEAN_ISENERGY,\n                Aspects.Read.Machine.BOOLEAN_CANEXTRACTENERGY,\n                Aspects.Read.Machine.BOOLEAN_CANINSERTENERGY,\n                Aspects.Read.Machine.BOOLEAN_ISENERGYFULL,\n                Aspects.Read.Machine.BOOLEAN_ISENERGYEMPTY,\n                Aspects.Read.Machine.BOOLEAN_ISENERGYNONEMPTY,\n                Aspects.Read.Machine.INTEGER_ENERGYSTORED,\n                Aspects.Read.Machine.INTEGER_ENERGYCAPACITY,\n                Aspects.Read.Machine.DOUBLE_ENERGYFILLRATIO\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeMachineReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeMachineReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeMachineReader> state) {\n        return GeneralConfig.machineReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeMachineWriter.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.write.PartStateWriterBase;\nimport org.cyclops.integrateddynamics.core.part.write.PartTypeWriteBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * A machine writer part.\n * @author josephcsible\n */\npublic class PartTypeMachineWriter extends PartTypeWriteBase<PartTypeMachineWriter, PartStateWriterBase<PartTypeMachineWriter>> {\n\n    public PartTypeMachineWriter(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n\n        ));\n    }\n\n    @Override\n    public PartStateWriterBase<PartTypeMachineWriter> constructDefaultState() {\n        return new PartStateWriterBase<PartTypeMachineWriter>(Aspects.REGISTRY.getAspects(this).size());\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateWriterBase<PartTypeMachineWriter> state) {\n        return GeneralConfig.machineWriterBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeNetworkReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * A reader part that can read aspects from the network it is contained in.\n * @author rubensworks\n */\npublic class PartTypeNetworkReader extends PartTypeReadBase<PartTypeNetworkReader, PartStateReaderBase<PartTypeNetworkReader>> {\n\n    public PartTypeNetworkReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Network.BOOLEAN_APPLICABLE,\n                Aspects.Read.Network.INTEGER_ELEMENT_COUNT,\n                Aspects.Read.Network.INTEGER_ENERGY_BATTERY_COUNT,\n                Aspects.Read.Network.LONG_ENERGY_STORED,\n                Aspects.Read.Network.LONG_ENERGY_MAX,\n                Aspects.Read.Network.INTEGER_ENERGY_CONSUMPTION_RATE,\n                Aspects.Read.Network.ANY_VALUE,\n                Aspects.Read.Network.OPERATOR_GETVARIABLEBYID\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeNetworkReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeNetworkReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeNetworkReader> state) {\n        return GeneralConfig.networkReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypePanelDisplay.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.part.panel.PartTypePanelVariableDriven;\n\n/**\n * A part that can display variables.\n * @author rubensworks\n */\npublic class PartTypePanelDisplay extends PartTypePanelVariableDriven<PartTypePanelDisplay, PartTypePanelDisplay.State> {\n\n    public PartTypePanelDisplay(String name) {\n        super(name);\n    }\n\n    @Override\n    public boolean supportsOffsets() {\n        return false;\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlockStatus(properties);\n    }\n\n    @Override\n    public PartTypePanelDisplay.State constructDefaultState() {\n        return new PartTypePanelDisplay.State();\n    }\n\n    @Override\n    public int getConsumptionRate(State state) {\n        return state.hasVariable() ? GeneralConfig.panelDisplayBaseConsumptionEnabled : GeneralConfig.panelDisplayBaseConsumptionDisabled;\n    }\n\n    @Override\n    public boolean forceLightTransparency(State state) {\n        return true;\n    }\n\n    public static class State extends PartTypePanelVariableDriven.State<PartTypePanelDisplay, PartTypePanelDisplay.State> {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypePanelLightDynamic.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.network.chat.Component;\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.state.BlockBehaviour;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.InvalidValueTypeException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.block.BlockInvisibleLight;\nimport org.cyclops.integrateddynamics.block.BlockInvisibleLightConfig;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeLightLevels;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.part.panel.PartTypePanelVariableDriven;\n\nimport javax.annotation.Nullable;\n\n/**\n * A part that can display variables.\n * @author rubensworks\n */\npublic class PartTypePanelLightDynamic extends PartTypePanelVariableDriven<PartTypePanelLightDynamic, PartTypePanelLightDynamic.State> {\n\n    public PartTypePanelLightDynamic(String name) {\n        super(name);\n    }\n\n    @Override\n    public boolean supportsOffsets() {\n        return false;\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlockStatus(properties);\n    }\n\n    @Override\n    public PartTypePanelLightDynamic.State constructDefaultState() {\n        return new PartTypePanelLightDynamic.State();\n    }\n\n    @Override\n    public int getConsumptionRate(State state) {\n        return GeneralConfig.panelLightDynamicBaseConsumption;\n    }\n\n    @Override\n    protected IgnoredBlockStatus.Status getStatus(PartTypePanelVariableDriven.State state) {\n        IgnoredBlockStatus.Status status = super.getStatus(state);\n        if (status == IgnoredBlockStatus.Status.ACTIVE && state.getDisplayValue() != null\n                && getLightLevel((State) state, state.getDisplayValue()) == 0) {\n            return IgnoredBlockStatus.Status.INACTIVE;\n        }\n        return status;\n    }\n\n    @Override\n    protected void onValueChanged(INetwork network, IPartNetwork partNetwork, PartTarget target, State state, IValue lastValue, IValue newValue) {\n        super.onValueChanged(network, partNetwork, target, state, lastValue, newValue);\n        int lightLevel = 0;\n        if(newValue != null) {\n            lightLevel = getLightLevel(state, newValue);\n        }\n        setLightLevel(target, lightLevel);\n        state.sendUpdate();\n    }\n\n    protected int getLightLevel(State state, IValue value) {\n        try {\n            return ValueTypeLightLevels.REGISTRY.getLightLevel(value);\n        } catch (InvalidValueTypeException e) {\n            state.addGlobalError(Component.translatable(L10NValues.PART_PANEL_ERROR_INVALIDTYPE,\n                    Component.translatable(value.getType().getTranslationKey())));\n        }\n        return 0;\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {\n        super.onNetworkRemoval(network, partNetwork, target, state);\n        PartTypePanelLightDynamic.setLightLevel(target, 0);\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {\n        super.onPostRemoved(network, partNetwork, target, state);\n        setLightLevel(target, 0);\n    }\n\n    @Override\n    public void onBlockNeighborChange(INetwork network, IPartNetwork partNetwork, PartTarget target, State state,\n                                      BlockGetter world, @Nullable Direction side) {\n        super.onBlockNeighborChange(network, partNetwork, target, state, world, side);\n        setLightLevel(target, state.getDisplayValue() == null ? 0 : getLightLevel(state, state.getDisplayValue()));\n    }\n\n    @Override\n    public void postUpdate(INetwork network, IPartNetwork partNetwork, PartTarget target, State state, boolean updated) {\n        boolean wasEnabled = isEnabled(state);\n        super.postUpdate(network, partNetwork, target, state, updated);\n        boolean isEnabled = isEnabled(state);\n        if(wasEnabled != isEnabled) {\n            setLightLevel(target, isEnabled ? getLightLevel(state, state.getDisplayValue()) : 0);\n        }\n    }\n\n    public static void setLightLevel(PartTarget target, int lightLevel) {\n        if (BlockInvisibleLightConfig.invisibleLightBlock) {\n            Level world = target.getTarget().getPos().getLevel(true);\n            BlockPos pos = target.getTarget().getPos().getBlockPos();\n            if(world.isEmptyBlock(pos) || world.getBlockState(pos).getBlock() == RegistryEntries.BLOCK_INVISIBLE_LIGHT.get()) {\n                if(lightLevel > 0) {\n                    world.setBlockAndUpdate(pos, RegistryEntries.BLOCK_INVISIBLE_LIGHT.get().defaultBlockState()\n                            .setValue(BlockInvisibleLight.LIGHT, lightLevel));\n                } else {\n                    world.setBlock(pos, Blocks.AIR.defaultBlockState(), IModHelpers.get().getMinecraftHelpers().getBlockNotifyClient());\n                }\n            }\n        } else {\n            IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(target.getCenter().getPos(), target.getCenter().getSide(), Capabilities.DynamicLight.BLOCK)\n                    .ifPresent(dynamicLight -> dynamicLight.setLightLevel(lightLevel));\n        }\n    }\n\n    public static class State extends PartTypePanelVariableDriven.State<PartTypePanelLightDynamic, PartTypePanelLightDynamic.State> {\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypePanelLightStatic.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockBehaviour;\nimport org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.core.block.IgnoredBlock;\nimport org.cyclops.integrateddynamics.core.part.PartStateEmpty;\nimport org.cyclops.integrateddynamics.core.part.panel.PartTypePanel;\n\nimport javax.annotation.Nullable;\n\n/**\n * A panel part that simply emits light.\n * @author rubensworks\n */\npublic class PartTypePanelLightStatic extends PartTypePanel<PartTypePanelLightStatic, PartStateEmpty<PartTypePanelLightStatic>> {\n\n    public static final int LIGHT_LEVEL = 15;\n\n    public PartTypePanelLightStatic(String name) {\n        super(name);\n    }\n\n    @Override\n    public boolean supportsOffsets() {\n        return false;\n    }\n\n    @Override\n    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {\n        return new IgnoredBlock(properties);\n    }\n\n    @Override\n    public PartStateEmpty<PartTypePanelLightStatic> constructDefaultState() {\n        return new PartStateEmpty<PartTypePanelLightStatic>();\n    }\n\n    @Override\n    public boolean isUpdate(PartStateEmpty<PartTypePanelLightStatic> state) {\n        return getConsumptionRate(state) > 0 || super.isUpdate(state);\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateEmpty<PartTypePanelLightStatic> state) {\n        return GeneralConfig.panelLightStaticBaseConsumption;\n    }\n\n    @Override\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, PartStateEmpty<PartTypePanelLightStatic> state) {\n        super.onNetworkAddition(network, partNetwork, target, state);\n        PartTypePanelLightDynamic.setLightLevel(target, LIGHT_LEVEL);\n    }\n\n    @Override\n    public void onBlockNeighborChange(INetwork network, IPartNetwork partNetwork, PartTarget target,\n                                      PartStateEmpty<PartTypePanelLightStatic> state, BlockGetter world, @Nullable Direction side) {\n        super.onBlockNeighborChange(network, partNetwork, target, state, world, side);\n        PartTypePanelLightDynamic.setLightLevel(target, LIGHT_LEVEL);\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, PartStateEmpty<PartTypePanelLightStatic> state) {\n        super.onNetworkRemoval(network, partNetwork, target, state);\n        PartTypePanelLightDynamic.setLightLevel(target, 0);\n    }\n\n    @Override\n    public void onPostRemoved(INetwork network, IPartNetwork partNetwork, PartTarget target, PartStateEmpty<PartTypePanelLightStatic> state) {\n        super.onPostRemoved(network, partNetwork, target, state);\n        PartTypePanelLightDynamic.setLightLevel(target, 0);\n    }\n\n    @Override\n    public void postUpdate(INetwork network, IPartNetwork partNetwork, PartTarget target, PartStateEmpty<PartTypePanelLightStatic> state, boolean updated) {\n        boolean wasEnabled = isEnabled(state);\n        super.postUpdate(network, partNetwork, target, state, updated);\n        boolean isEnabled = isEnabled(state);\n        if(wasEnabled != isEnabled) {\n            PartTypePanelLightDynamic.setLightLevel(target, isEnabled ? LIGHT_LEVEL : 0);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeRedstoneReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\nimport org.cyclops.integrateddynamics.part.aspect.read.redstone.IReadRedstoneComponent;\nimport org.cyclops.integrateddynamics.part.aspect.read.redstone.ReadRedstoneComponent;\n\n/**\n * A redstone reader part.\n * @author rubensworks\n */\npublic class PartTypeRedstoneReader extends PartTypeReadBase<PartTypeRedstoneReader, PartStateReaderBase<PartTypeRedstoneReader>> {\n\n    private static final IReadRedstoneComponent READ_REDSTONE_COMPONENT = new ReadRedstoneComponent();\n\n    public PartTypeRedstoneReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.Redstone.BOOLEAN_LOW,\n                Aspects.Read.Redstone.BOOLEAN_NONLOW,\n                Aspects.Read.Redstone.BOOLEAN_HIGH,\n                Aspects.Read.Redstone.BOOLEAN_CLOCK,\n                Aspects.Read.Redstone.INTEGER_VALUE,\n                Aspects.Read.Redstone.INTEGER_COMPARATOR\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeRedstoneReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeRedstoneReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeRedstoneReader> state) {\n        return GeneralConfig.redstoneReaderBaseConsumption;\n    }\n\n    @Override\n    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, PartStateReaderBase<PartTypeRedstoneReader> state) {\n        super.onNetworkAddition(network, partNetwork, target, state);\n        READ_REDSTONE_COMPONENT.setAllowRedstoneInput(target, true);\n    }\n\n    @Override\n    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, PartStateReaderBase<PartTypeRedstoneReader> state) {\n        super.onNetworkRemoval(network, partNetwork, target, state);\n        READ_REDSTONE_COMPONENT.setAllowRedstoneInput(target, false);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeRedstoneWriter.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.write.PartStateWriterBase;\nimport org.cyclops.integrateddynamics.core.part.write.PartTypeWriteBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * A redstone writer part.\n * @author rubensworks\n */\npublic class PartTypeRedstoneWriter extends PartTypeWriteBase<PartTypeRedstoneWriter, PartStateWriterBase<PartTypeRedstoneWriter>> {\n\n    public PartTypeRedstoneWriter(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Write.Redstone.BOOLEAN,\n                Aspects.Write.Redstone.INTEGER,\n                Aspects.Write.Redstone.BOOLEAN_PULSE,\n                Aspects.Write.Redstone.INTEGER_PULSE\n        ));\n    }\n\n    @Override\n    public PartStateWriterBase<PartTypeRedstoneWriter> constructDefaultState() {\n        return new PartStateWriterBase<PartTypeRedstoneWriter>(Aspects.REGISTRY.getAspects(this).size());\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateWriterBase<PartTypeRedstoneWriter> state) {\n        return GeneralConfig.redstoneWriterBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/PartTypeWorldReader.java",
    "content": "package org.cyclops.integrateddynamics.part;\n\nimport com.google.common.collect.Lists;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.core.part.aspect.AspectRegistry;\nimport org.cyclops.integrateddynamics.core.part.read.PartStateReaderBase;\nimport org.cyclops.integrateddynamics.core.part.read.PartTypeReadBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * An world reader part.\n * @author rubensworks\n */\npublic class PartTypeWorldReader extends PartTypeReadBase<PartTypeWorldReader, PartStateReaderBase<PartTypeWorldReader>> {\n\n    public PartTypeWorldReader(String name) {\n        super(name);\n        AspectRegistry.getInstance().register(this, Lists.<IAspect>newArrayList(\n                Aspects.Read.World.BOOLEAN_WEATHER_CLEAR,\n                Aspects.Read.World.BOOLEAN_WEATHER_RAINING,\n                Aspects.Read.World.BOOLEAN_WEATHER_THUNDER,\n                Aspects.Read.World.BOOLEAN_ISDAY,\n                Aspects.Read.World.BOOLEAN_ISNIGHT,\n                Aspects.Read.World.INTEGER_RAINCOUNTDOWN,\n                Aspects.Read.World.INTEGER_TICKTIME,\n                Aspects.Read.World.INTEGER_DAYTIME,\n                Aspects.Read.World.INTEGER_LIGHTLEVEL,\n                Aspects.Read.World.DOUBLE_TPS,\n                Aspects.Read.World.LONG_TIME,\n                Aspects.Read.World.LONG_TOTALTIME,\n                Aspects.Read.World.STRING_NAME,\n                Aspects.Read.World.LIST_PLAYERS\n        ));\n    }\n\n    @Override\n    public PartStateReaderBase<PartTypeWorldReader> constructDefaultState() {\n        return new PartStateReaderBase<PartTypeWorldReader>();\n    }\n\n    @Override\n    public int getConsumptionRate(PartStateReaderBase<PartTypeWorldReader> state) {\n        return GeneralConfig.worldReaderBaseConsumption;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/AspectBase.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.SimpleContainer;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.part.*;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspect;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.helper.PartHelpers;\nimport org.cyclops.integrateddynamics.core.inventory.container.ContainerAspectSettings;\nimport org.cyclops.integrateddynamics.core.part.PartTypeBase;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\n/**\n * Base class for aspects.\n * @author rubensworks\n */\npublic abstract class AspectBase<V extends IValue, T extends IValueType<V>> implements IAspect<V, T> {\n\n    private final IAspectProperties defaultProperties;\n\n    private final String modId;\n    private String translationKey = null;\n\n    public AspectBase(String modId, IAspectProperties defaultProperties) {\n        this.modId = modId;\n        this.defaultProperties = defaultProperties == null ? createDefaultProperties() : defaultProperties;\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(getModId(), getUnlocalizedType().replaceAll(\"\\\\.\", \"_\"));\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return translationKey != null ? translationKey : (translationKey = getUnlocalizedPrefix());\n    }\n\n    protected String getUnlocalizedPrefix() {\n        return \"aspect.\" + getModId() + \".\" + getUnlocalizedType();\n    }\n\n    protected abstract String getUnlocalizedType();\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo) {\n        Component aspectName = Component.translatable(getTranslationKey());\n        Component valueTypeName = Component.translatable(getValueType().getTranslationKey());\n        tooltipAdder.accept(Component.translatable(L10NValues.ASPECT_TOOLTIP_ASPECTNAME, aspectName));\n        tooltipAdder.accept(Component.translatable(L10NValues.ASPECT_TOOLTIP_VALUETYPENAME, valueTypeName)\n                .withStyle(getValueType().getDisplayColorFormat()));\n        if(appendOptionalInfo) {\n            IModHelpers.get().getL10NHelpers().addOptionalInfo(tooltipAdder, getUnlocalizedPrefix(), net.minecraft.world.item.TooltipFlag.NORMAL);\n        }\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> boolean hasProperties() {\n        return getDefaultProperties() != null;\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> IAspectProperties getProperties(P partType, PartTarget target, S state) {\n        IAspectProperties properties = state.getAspectProperties(this);\n        if(properties == null) {\n            properties = getDefaultProperties().clone();\n            setProperties(partType, target, state, properties);\n        }\n        return properties;\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void setProperties(P partType, PartTarget target, S state, IAspectProperties properties) {\n        state.setAspectProperties(this, properties);\n    }\n\n    @Override\n    public final IAspectProperties getDefaultProperties() {\n        return defaultProperties;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public Collection<IAspectPropertyTypeInstance> getPropertyTypes() {\n        return hasProperties() ? getDefaultProperties().getTypes() : Collections.emptyList();\n    }\n\n    @Override\n    public MenuProvider getPropertiesContainerProvider(PartPos pos) {\n        return new MenuProvider() {\n            @Override\n            public Component getDisplayName() {\n                return Component.translatable(\"gui.integrateddynamics.aspect_settings\");\n            }\n\n            @Nullable\n            @Override\n            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {\n                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);\n                return new ContainerAspectSettings(id, playerInventory, new SimpleContainer(0),\n                        Optional.of(data.getRight()), Optional.of(data.getLeft()), Optional.of(data.getMiddle()), AspectBase.this);\n            }\n\n            @Override\n            public boolean shouldTriggerClientSideContainerClosingOnOpen() {\n                return false;\n            }\n        };\n    }\n\n    /**\n     * Creates the default properties for this aspect, only called once.\n     * @return The default properties.\n     */\n    @Deprecated\n    protected IAspectProperties createDefaultProperties() {\n        return null;\n    }\n\n    protected String getModId() {\n        return this.modId;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/Aspects.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.math.DoubleMath;\nimport com.google.common.math.Stats;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Holder;\nimport net.minecraft.core.particles.*;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.protocol.game.ClientboundSoundPacket;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.server.MinecraftServer;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.sounds.SoundEvent;\nimport net.minecraft.sounds.SoundSource;\nimport net.minecraft.util.StringUtil;\nimport net.minecraft.world.entity.EntitySelector;\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.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.NoteBlockInstrument;\nimport net.minecraft.world.level.storage.ServerLevelData;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.Vec3;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport net.neoforged.neoforge.transfer.EmptyResourceHandler;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.transaction.Transaction;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.cyclopscore.network.IPacketHandler;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.GeneralConfig;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.IValueInterface;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.network.IEnergyConsumingNetworkElement;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.AspectUpdateType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRegistry;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.*;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.EnergyHelpers;\nimport org.cyclops.integrateddynamics.core.helper.Helpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.AspectBuilder;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.IAspectValuePropagator;\nimport org.cyclops.integrateddynamics.network.packet.SpeakTextPacket;\nimport org.cyclops.integrateddynamics.part.aspect.read.AspectReadBuilders;\nimport org.cyclops.integrateddynamics.part.aspect.write.AspectWriteBuilders;\n\nimport java.util.*;\n\n/**\n * Collection of all aspects.\n * @author rubensworks\n */\npublic class Aspects {\n\n    public static final IAspectRegistry REGISTRY = IntegratedDynamics._instance.getRegistryManager().getRegistry(IAspectRegistry.class);\n\n    public static void load() {}\n\n    public static final class Read {\n\n        public static final class Audio {\n\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_HARP_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.HARP)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BASEDRUM_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.BASEDRUM)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_SNARE_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.SNARE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_HAT_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.HAT)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BASS_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.BASS)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_FLUTE_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.FLUTE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BELL_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.BELL)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_GUITAR_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.GUITAR)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_CHIME_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.CHIME)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_XYLOPHONE_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.XYLOPHONE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_IRON_XYLOPHONE_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.IRON_XYLOPHONE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_COW_BELL_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.COW_BELL)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_DIDGERIDOO_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.DIDGERIDOO)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BIT_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.BIT)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BANJO_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.BANJO)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_PLING_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.PLING)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_ZOMBIE_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.ZOMBIE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_SKELETON_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.SKELETON)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_CREEPER_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.CREEPER)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_DRAGON_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.DRAGON)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_WITHER_SKELETON_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.WITHER_SKELETON)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_PIGLIN_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.PIGLIN)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_CUSTOM_HEAD_NOTE =\n                    AspectReadBuilders.Audio.forInstrument(NoteBlockInstrument.CUSTOM_HEAD)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER).buildRead();\n\n        }\n\n        public static final class Block {\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_BLOCK =\n                    AspectReadBuilders.Block.BUILDER_BOOLEAN.handle(\n                        dimPos -> dimPos.getLevel(true).getBlockState(dimPos.getBlockPos()).getBlock() != Blocks.AIR\n                    ).withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"block\").buildRead();\n            public static final IAspectRead<ValueTypeString.ValueString, ValueTypeString> STRING_DIMENSION =\n                    AspectReadBuilders.Block.BUILDER_STRING.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                            world -> world.dimension().identifier().toString()\n                    ).withUpdateType(AspectUpdateType.NEVER)\n                            .handle(AspectReadBuilders.PROP_GET_STRING, \"dimension\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_POSX =\n                    AspectReadBuilders.Block.BUILDER_INTEGER.handle(AspectReadBuilders.World.PROP_GET_POS).handle(\n                        BlockPos::getX\n                    ).withUpdateType(AspectUpdateType.NEVER)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"posx\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_POSY =\n                    AspectReadBuilders.Block.BUILDER_INTEGER.handle(AspectReadBuilders.World.PROP_GET_POS).handle(\n                        BlockPos::getY\n                    ).withUpdateType(AspectUpdateType.NEVER)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"posy\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_POSZ =\n                    AspectReadBuilders.Block.BUILDER_INTEGER.handle(AspectReadBuilders.World.PROP_GET_POS).handle(\n                        BlockPos::getZ\n                    ).withUpdateType(AspectUpdateType.NEVER)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"posz\").buildRead();\n            public static final IAspectRead<ValueObjectTypeBlock.ValueBlock, ValueObjectTypeBlock> BLOCK =\n                    AspectReadBuilders.Block.BUILDER_BLOCK\n                            .handle(\n                        dimPos -> dimPos.getLevel(true).getBlockState(dimPos.getBlockPos())\n                    ).withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_BLOCK).buildRead();\n            public static final IAspectRead<ValueTypeNbt.ValueNbt, ValueTypeNbt> NBT =\n                    AspectReadBuilders.Block.BUILDER_NBT.handle(dimPos -> {\n                        BlockEntity tile = dimPos.getLevel(true).getBlockEntity(dimPos.getBlockPos());\n                        try {\n                            if (tile != null) {\n                                return Optional.<Tag>of(tile.saveWithFullMetadata(tile.getLevel().registryAccess()));\n                            }\n                        } catch (Exception e) {\n                            // Catch possible errors\n                        }\n                        return Optional.<Tag>empty();\n                    }).handle(AspectReadBuilders.PROP_GET_NBT, \"tile\").buildRead();\n            public static final IAspectRead<ValueTypeString.ValueString, ValueTypeString> STRING_BIOME =\n                    AspectReadBuilders.Block.BUILDER_STRING\n                            .handle(\n                                    // Copied from DebugScreenOverlay#printBiome\n                                    dimPos -> dimPos.getLevel(true).getBiome(dimPos.getBlockPos())\n                                            .unwrap().map(key -> key.identifier().toString(), biome -> \"[unregistered \" + biome + \"]\")\n                            ).withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_STRING, \"biome\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_LIGHT =\n                    AspectReadBuilders.Block.BUILDER_INTEGER.handle(dimPos -> dimPos.getLevel(true).getMaxLocalRawBrightness(dimPos.getBlockPos()))\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"light\").buildRead();\n        }\n\n        public static final class Entity {\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_ITEMFRAMEROTATION =\n                    AspectReadBuilders.Entity.BUILDER_INTEGER_ALL\n                            .handle(AspectReadBuilders.World.PROP_GET_ITEMFRAME)\n                            .handle(itemFrame -> itemFrame != null ? itemFrame.getRotation() : 0)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"itemframerotation\").buildRead();\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_ENTITIES =\n                    AspectReadBuilders.Entity.BUILDER_LIST.handle(dimPos -> {\n                        List<net.minecraft.world.entity.Entity> entities = dimPos.getLevel(true).getEntities((net.minecraft.world.entity.Entity) null,\n                                new AABB(Vec3.atLowerCornerOf(dimPos.getBlockPos()), Vec3.atLowerCornerOf(dimPos.getBlockPos().offset(1, 1, 1))), EntitySelector.NO_SPECTATORS);\n                        return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, Lists.transform(entities,\n                            ValueObjectTypeEntity.ValueEntity::of\n                        ));\n                    }).appendKind(\"entities\").buildRead();\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_PLAYERS =\n                    AspectReadBuilders.Entity.BUILDER_LIST.handle(dimPos -> {\n                        List<net.minecraft.world.entity.Entity> entities = dimPos.getLevel(true).getEntities((net.minecraft.world.entity.Entity) null,\n                                new AABB(Vec3.atLowerCornerOf(dimPos.getBlockPos()), Vec3.atLowerCornerOf(dimPos.getBlockPos().offset(1, 1, 1))), Helpers.SELECTOR_IS_PLAYER);\n                        return ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, Lists.transform(entities,\n                            ValueObjectTypeEntity.ValueEntity::of\n                        ));\n                    }).appendKind(\"players\").buildRead();\n\n            public static final IAspectRead<ValueObjectTypeEntity.ValueEntity, ValueObjectTypeEntity> ENTITY =\n                    AspectReadBuilders.Entity.BUILDER_ENTITY.withProperties(AspectReadBuilders.LIST_PROPERTIES).handle(input -> {\n                        int i = input.getRight().getValue(AspectReadBuilders.PROPERTY_LISTINDEX).getRawValue();\n                        DimPos dimPos = input.getLeft().getTarget().getPos();\n                        List<net.minecraft.world.entity.Entity> entities = dimPos.getLevel(true).getEntities((net.minecraft.world.entity.Entity) null,\n                                new AABB(Vec3.atLowerCornerOf(dimPos.getBlockPos()), Vec3.atLowerCornerOf(dimPos.getBlockPos().offset(1, 1, 1))), EntitySelector.NO_SPECTATORS);\n                        return ValueObjectTypeEntity.ValueEntity.of(i < entities.size() ? entities.get(i) : null);\n                    }).buildRead();\n\n            public static final IAspectRead<ValueObjectTypeItemStack.ValueItemStack, ValueObjectTypeItemStack> ITEMSTACK_ITEMFRAMECONTENTS =\n                    AspectReadBuilders.Entity.BUILDER_ITEMSTACK\n                            .handle(AspectReadBuilders.World.PROP_GET_ITEMFRAME)\n                            .handle(itemFrame -> itemFrame != null ? itemFrame.getItem() : ItemStack.EMPTY)\n                            .handle(AspectReadBuilders.PROP_GET_ITEMSTACK, \"itemframecontents\").buildRead();\n        }\n\n        public static final class ExtraDimensional {\n\n            private static final Random RANDOM = new Random();\n\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_RANDOM =\n                    AspectReadBuilders.ExtraDimensional.BUILDER_INTEGER.handle(\n                        minecraft -> RANDOM.nextInt()\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"random\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_PLAYERCOUNT =\n                    AspectReadBuilders.ExtraDimensional.BUILDER_INTEGER.handle(\n                        MinecraftServer::getPlayerCount\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"playercount\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_TICKTIME =\n                    AspectReadBuilders.ExtraDimensional.BUILDER_INTEGER.handle(\n                        minecraft -> (int) DoubleMath.mean(minecraft.getAverageTickTimeNanos())\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"ticktime\").buildRead();\n\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_TPS =\n                    AspectReadBuilders.ExtraDimensional.BUILDER_DOUBLE.handle(\n                            minecraft -> Math.min(20, Stats.meanOf(minecraft.getTickTimesNanos()) / 1000)\n                    ).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"tps\").buildRead();\n\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_PLAYERS =\n                    AspectReadBuilders.ExtraDimensional.BUILDER_LIST.handle(\n                        minecraft -> ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, Lists.transform(minecraft.getPlayerList().getPlayers(),\n                                ValueObjectTypeEntity.ValueEntity::of))\n                    ).appendKind(\"players\").buildRead();\n\n        }\n\n        public static final class Fluid {\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_FULL =\n                    AspectReadBuilders.Fluid.BUILDER_BOOLEAN.handle(tankInfo -> {\n                        boolean allFull = true;\n                        for (int i = 0; i < tankInfo.size(); i++) {\n                            if (tankInfo.getResource(i).isEmpty() && tankInfo.getCapacityAsLong(i, FluidResource.EMPTY) > 0\n                                    || (!tankInfo.getResource(i).isEmpty() && tankInfo.getAmountAsLong(i) < tankInfo.getCapacityAsLong(i, FluidResource.EMPTY))) {\n                                allFull = false;\n                            }\n                        }\n                        return allFull;\n                    }).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"full\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_EMPTY =\n                    AspectReadBuilders.Fluid.BUILDER_BOOLEAN.handle(tankInfo -> {\n                        for (int i = 0; i < tankInfo.size(); i++) {\n                            if (!tankInfo.getResource(i).isEmpty() && tankInfo.getCapacityAsLong(i, FluidResource.EMPTY) > 0\n                                    || (!tankInfo.getResource(i).isEmpty() && tankInfo.getAmountAsLong(i) < tankInfo.getCapacityAsLong(i, FluidResource.EMPTY))) {\n                                return false;\n                            }\n                        }\n                        return true;\n                    }).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"empty\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_NONEMPTY =\n                    AspectReadBuilders.Fluid.BUILDER_BOOLEAN.handle(tankInfo -> {\n                        boolean hasFluid = false;\n                        for (int i = 0; i < tankInfo.size(); i++) {\n                            if (!tankInfo.getResource(i).isEmpty() && tankInfo.getAmountAsLong(i) > 0) {\n                                hasFluid = true;\n                            }\n                        }\n                        return hasFluid;\n                    }).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"nonempty\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_APPLICABLE =\n                    AspectReadBuilders.Fluid.BUILDER_BOOLEAN.handle(\n                        tankInfo -> tankInfo.size() > 0 && tankInfo != EmptyResourceHandler.<FluidResource>instance()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"applicable\").buildRead();\n\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_AMOUNT =\n                    AspectReadBuilders.Fluid.BUILDER_LONG_ACTIVATABLE.handle(tankInfo -> tankInfo != null ? tankInfo.getLeft().getAmountAsLong(tankInfo.getRight()) : 0L\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"amount\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_AMOUNTTOTAL =\n                    AspectReadBuilders.Fluid.BUILDER_LONG.handle(tankInfo -> {\n                        long amount = 0;\n                        for (int i = 0; i < tankInfo.size(); i++) {\n                            try {\n                                amount = Math.addExact(amount, tankInfo.getAmountAsLong(i));\n                            } catch (ArithmeticException e) {\n                                amount = Long.MAX_VALUE;\n                            }\n                            if (amount == Integer.MAX_VALUE) {\n                                break;\n                            }\n                        }\n                        return amount;\n                    }).handle(AspectReadBuilders.PROP_GET_LONG, \"totalamount\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_CAPACITY =\n                    AspectReadBuilders.Fluid.BUILDER_LONG_ACTIVATABLE.handle(\n                        tankInfo -> tankInfo != null ? tankInfo.getLeft().getCapacityAsLong(tankInfo.getRight(), FluidResource.EMPTY) : 0\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"capacity\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_CAPACITYTOTAL =\n                    AspectReadBuilders.Fluid.BUILDER_LONG.handle(tankInfo -> {\n                        long capacity = 0;\n                        for (int i = 0; i < tankInfo.size(); i++) {\n                            try {\n                                capacity = Math.addExact(capacity, tankInfo.getCapacityAsLong(i, FluidResource.EMPTY));\n                            } catch (ArithmeticException e) {\n                                capacity = Long.MAX_VALUE;\n                            }\n                            if (capacity == Long.MAX_VALUE) {\n                                break;\n                            }\n                        }\n                        return capacity;\n                    }).handle(AspectReadBuilders.PROP_GET_LONG, \"totalcapacity\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_TANKS =\n                    AspectReadBuilders.Fluid.BUILDER_INTEGER.handle(\n                            ResourceHandler::size\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"tanks\").buildRead();\n\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_FILLRATIO =\n                    AspectReadBuilders.Fluid.BUILDER_DOUBLE_ACTIVATABLE.handle(tankInfo -> {\n                        if(tankInfo == null) {\n                            return 0D;\n                        }\n                        double amount = tankInfo.getLeft().getAmountAsLong(tankInfo.getRight());\n                        return amount / (double) tankInfo.getLeft().getCapacityAsLong(tankInfo.getRight(), FluidResource.EMPTY);\n                    }).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"fillratio\").buildRead();\n\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_TANKFLUIDS =\n                    AspectReadBuilders.BUILDER_LIST.appendKind(\"fluid\").handle(AspectReadBuilders.Fluid.PROP_GET_LIST_FLUIDSTACKS, \"fluidstacks\").buildRead();\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_TANKCAPACITIES =\n                    AspectReadBuilders.BUILDER_LIST.appendKind(\"fluid\").handle(AspectReadBuilders.Fluid.PROP_GET_LIST_CAPACITIES, \"capacities\").buildRead();\n\n            public static final IAspectRead<ValueObjectTypeFluidStack.ValueFluidStack, ValueObjectTypeFluidStack> FLUIDSTACK =\n                    AspectReadBuilders.BUILDER_OBJECT_FLUIDSTACK\n                            .handle(AspectReadBuilders.Fluid.PROP_GET_ACTIVATABLE, \"fluid\").withProperties(AspectReadBuilders.Fluid.PROPERTIES)\n                            .handle(AspectReadBuilders.Fluid.PROP_GET_FLUIDSTACK).handle(AspectReadBuilders.PROP_GET_FLUIDSTACK).buildRead();\n\n            public static final IAspectRead<ValueObjectTypeFluidStack.ValueFluidStack, ValueObjectTypeFluidStack> BLOCK =\n                    AspectReadBuilders.BUILDER_OBJECT_FLUIDSTACK\n                            .handle(AspectReadBuilders.Block.PROP_GET, \"block\")\n                            .handle(dimPos -> {\n                                BlockState blockState = dimPos.getLevel(true).getBlockState(dimPos.getBlockPos());\n                                net.minecraft.world.level.block.Block block = blockState.getBlock();\n                                if (block instanceof LiquidBlock) {\n                                    return new FluidStack(((LiquidBlock) block).fluid, IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());\n                                }\n                                return FluidStack.EMPTY;\n                            })\n                            .handle(AspectReadBuilders.PROP_GET_FLUIDSTACK).buildRead();\n\n        }\n\n        public static final class Inventory {\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_FULL =\n                    AspectReadBuilders.Inventory.BUILDER_BOOLEAN.handle(inventory -> {\n                        if(inventory != null) {\n                            for (int i = 0; i < inventory.size(); i++) {\n                                ItemStack itemStack = inventory.getResource(i).toStack(inventory.getAmountAsInt(i));\n                                if (itemStack.isEmpty()) {\n                                    return false;\n                                }\n                            }\n                        }\n                        return true;\n                    }).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"full\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_EMPTY =\n                    AspectReadBuilders.Inventory.BUILDER_BOOLEAN.handle(inventory -> {\n                        if(inventory != null) {\n                            for(int i = 0; i < inventory.size(); i++) {\n                                ItemStack itemStack = inventory.getResource(i).toStack(inventory.getAmountAsInt(i));\n                                if(!itemStack.isEmpty()) {\n                                    return false;\n                                }\n                            }\n                        }\n                        return true;\n                    }).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"empty\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_NONEMPTY =\n                    AspectReadBuilders.Inventory.BUILDER_BOOLEAN.handle(inventory -> {\n                        if(inventory != null) {\n                            for(int i = 0; i < inventory.size(); i++) {\n                                ItemStack itemStack = inventory.getResource(i).toStack(inventory.getAmountAsInt(i));\n                                if(!itemStack.isEmpty()) {\n                                    return true;\n                                }\n                            }\n                        }\n                        return false;\n                    }).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"nonempty\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_APPLICABLE =\n                    AspectReadBuilders.Inventory.BUILDER_BOOLEAN.handle(\n                        Objects::nonNull\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"applicable\").buildRead();\n\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_COUNT =\n                    AspectReadBuilders.Inventory.BUILDER_INTEGER.handle(inventory -> {\n                        int count = 0;\n                        if(inventory != null) {\n                            for (int i = 0; i < inventory.size(); i++) {\n                                ItemStack itemStack = inventory.getResource(i).toStack(inventory.getAmountAsInt(i));\n                                if (!itemStack.isEmpty()) {\n                                    count += itemStack.getCount();\n                                }\n                            }\n                        }\n                        return count;\n                    }).handle(AspectReadBuilders.PROP_GET_INTEGER, \"count\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_SLOTS =\n                    AspectReadBuilders.Inventory.BUILDER_INTEGER.handle(\n                        inventory -> inventory != null ? inventory.size() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"slots\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_SLOTSFILLED =\n                    AspectReadBuilders.Inventory.BUILDER_INTEGER.handle(inventory -> {\n                        int count = 0;\n                        if(inventory != null) {\n                            for (int i = 0; i < inventory.size(); i++) {\n                                ItemStack itemStack = inventory.getResource(i).toStack(inventory.getAmountAsInt(i));\n                                if (!itemStack.isEmpty()) {\n                                    count++;\n                                }\n                            }\n                        }\n                        return count;\n                    }).handle(AspectReadBuilders.PROP_GET_INTEGER, \"slotsfilled\").buildRead();\n\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_FILLRATIO =\n                    AspectReadBuilders.Inventory.BUILDER_DOUBLE.handle(inventory -> {\n                        int count = 0;\n                        if(inventory != null) {\n                            for (int i = 0; i < inventory.size(); i++) {\n                                ItemStack itemStack = inventory.getResource(i).toStack(inventory.getAmountAsInt(i));\n                                if (!itemStack.isEmpty()) {\n                                    count++;\n                                }\n                            }\n                        }\n                        return ((double) count) / (double) (inventory != null ? inventory.size() : 1);\n                    }).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"fillratio\").buildRead();\n\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_ITEMSTACKS =\n                    AspectReadBuilders.BUILDER_LIST.appendKind(\"inventory\")\n                            .handle(AspectReadBuilders.Inventory.PROP_GET_LIST, \"itemstacks\").buildRead();\n\n            public static final IAspectRead<ValueObjectTypeItemStack.ValueItemStack, ValueObjectTypeItemStack> OBJECT_ITEM_STACK_SLOT =\n                    AspectReadBuilders.Inventory.BUILDER_ITEMSTACK.handle(AspectReadBuilders.PROP_GET_ITEMSTACK).buildRead();\n\n        }\n\n        public static final class Machine {\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISWORKER =\n                    AspectReadBuilders.Machine.BUILDER_WORKER_BOOLEAN.handle(\n                        Objects::nonNull\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isworker\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_HASWORK =\n                    AspectReadBuilders.Machine.BUILDER_WORKER_BOOLEAN.handle(\n                        worker -> worker != null && worker.hasWork()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"haswork\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_CANWORK =\n                    AspectReadBuilders.Machine.BUILDER_WORKER_BOOLEAN.handle(\n                        worker -> worker != null && worker.canWork()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"canwork\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISWORKING =\n                    AspectReadBuilders.Machine.BUILDER_WORKER_BOOLEAN.handle(\n                        worker -> worker != null && worker.canWork() && worker.hasWork()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isworking\").buildRead();\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISTEMPERATURE =\n                    AspectReadBuilders.Machine.BUILDER_TEMPERATURE_BOOLEAN.handle(\n                        Objects::nonNull\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"istemperature\").buildRead();\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_TEMPERATURE =\n                    AspectReadBuilders.Machine.BUILDER_TEMPERATURE_DOUBLE.handle(\n                        temperature -> temperature != null ? temperature.getTemperature() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"temperature\").buildRead();\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_MAXTEMPERATURE =\n                    AspectReadBuilders.Machine.BUILDER_TEMPERATURE_DOUBLE.handle(\n                        temperature -> temperature != null ? temperature.getMaximumTemperature() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"maxtemperature\").buildRead();\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_MINTEMPERATURE =\n                    AspectReadBuilders.Machine.BUILDER_TEMPERATURE_DOUBLE.handle(\n                        temperature -> temperature != null ? temperature.getMinimumTemperature() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"mintemperature\").buildRead();\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_DEFAULTTEMPERATURE =\n                    AspectReadBuilders.Machine.BUILDER_TEMPERATURE_DOUBLE.handle(\n                        temperature -> temperature != null ? temperature.getDefaultTemperature() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"defaulttemperature\").buildRead();\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISRECIPEHANDLER =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_BOOLEAN\n                            .handle(Objects::nonNull)\n                            .handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"applicable\").buildRead();\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_GETRECIPES =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_LIST.handle(\n                            input -> ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyPositionedRecipes(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()))).appendKind(\"recipes\").buildRead();\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator> OPERATOR_GETRECIPEOUTPUT =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_OPERATOR.handle(\n                            input -> ValueTypeOperator.ValueOperator.of(new PositionedOperatorRecipeHandlerOutput<>(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                            ))).appendKind(\"recipeoutputbyinput\").buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorRecipeHandlerOutput.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipe_handler_output\")));\n            }\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator> OPERATOR_GETRECIPEINPUTS =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_OPERATOR.handle(input ->\n                            ValueTypeOperator.ValueOperator.of(new PositionedOperatorRecipeHandlerInputs<>(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                            ))).appendKind(\"recipeinputsbyoutput\").buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorRecipeHandlerInputs.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipe_handler_inputs\")));\n            }\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator> OPERATOR_GETRECIPESBYINPUT =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_OPERATOR.handle(\n                            input -> ValueTypeOperator.ValueOperator.of(new PositionedOperatorRecipeHandlerRecipesByInput<>(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                            ))).appendKind(\"recipesbyinput\").buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorRecipeHandlerRecipesByInput.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipe_handler_recipes_by_input\")));\n            }\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator> OPERATOR_GETRECIPESBYOUTPUT =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_OPERATOR.handle(\n                            input -> ValueTypeOperator.ValueOperator.of(new PositionedOperatorRecipeHandlerRecipesByOutput<>(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                            ))).appendKind(\"recipesbyoutput\").buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorRecipeHandlerRecipesByOutput.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipe_handler_recipes_by_output\")));\n            }\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator> OPERATOR_GETRECIPEBYINPUT =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_OPERATOR.handle(\n                            input -> ValueTypeOperator.ValueOperator.of(new PositionedOperatorRecipeHandlerRecipeByInput<>(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                            ))).appendKind(\"recipebyinput\").buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorRecipeHandlerRecipeByInput.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipe_handler_recipe_by_input\")));\n            }\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator> OPERATOR_GETRECIPEBYOUTPUT =\n                    AspectReadBuilders.Machine.BUILDER_RECIPE_HANDLER_OPERATOR.handle(\n                            input -> ValueTypeOperator.ValueOperator.of(new PositionedOperatorRecipeHandlerRecipeByOutput<>(\n                                    input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                            ))).appendKind(\"recipebyoutput\").buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorRecipeHandlerRecipeByOutput.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_recipe_handler_recipe_by_output\")));\n            }\n\n            public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, EnergyHandler>\n                    PROP_GET = input -> EnergyHelpers.getEnergyStorage(input.getLeft().getTarget()).orElse(null);\n\n            public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, EnergyHandler>\n                    BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET, \"energy\");\n            public static final AspectBuilder<ValueTypeLong.ValueLong, ValueTypeLong, EnergyHandler>\n                    BUILDER_LONG = AspectReadBuilders.BUILDER_LONG.handle(PROP_GET, \"energy\");\n            public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, EnergyHandler>\n                    BUILDER_DOUBLE = AspectReadBuilders.BUILDER_DOUBLE.handle(PROP_GET, \"energy\");\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISENERGY =\n                    BUILDER_BOOLEAN.handle(\n                        Objects::nonNull\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"applicable\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_CANEXTRACTENERGY =\n                    BUILDER_BOOLEAN.handle(\n                        data -> {\n                            if (data != null) {\n                                try (var tx = Transaction.openRoot()) {\n                                    return data.extract(1, tx) == 1;\n                                }\n                            }\n                            return false;\n                        }\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"canextract\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_CANINSERTENERGY =\n                    BUILDER_BOOLEAN.handle(\n                        data -> {\n                            if (data != null) {\n                                try (var tx = Transaction.openRoot()) {\n                                    return data.insert(1, tx) == 1;\n                                }\n                            }\n                            return false;\n                        }\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"caninsert\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISENERGYFULL =\n                    BUILDER_BOOLEAN.handle(\n                        data -> data != null && data.getAmountAsLong() == data.getCapacityAsLong()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isfull\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISENERGYEMPTY =\n                    BUILDER_BOOLEAN.handle(\n                        data -> data != null && data.getAmountAsLong() == 0\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isempty\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISENERGYNONEMPTY =\n                    BUILDER_BOOLEAN.handle(\n                        data -> data != null && data.getAmountAsLong() != 0\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isnonempty\").buildRead();\n\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> INTEGER_ENERGYSTORED =\n                    BUILDER_LONG.handle(\n                        data -> data != null ? data.getAmountAsLong() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"amount\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> INTEGER_ENERGYCAPACITY =\n                    BUILDER_LONG.handle(\n                        data -> data != null ? data.getCapacityAsLong() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"capacity\").buildRead();\n\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_ENERGYFILLRATIO =\n                    BUILDER_DOUBLE.handle(data -> {\n                        if(data != null) {\n                            double capacity = (double) data.getCapacityAsLong();\n                            if(capacity == 0.0D) {\n                                return 0.0D;\n                            }\n                            return ((double) data.getAmountAsLong()) / capacity;\n                        }\n                        return 0.0D;\n                    }).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"fillratio\").buildRead();\n\n        }\n\n        public static final class Network {\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_APPLICABLE =\n                    AspectReadBuilders.Network.BUILDER_BOOLEAN.handle(\n                        Objects::nonNull\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"applicable\").buildRead();\n\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_ELEMENT_COUNT =\n                    AspectReadBuilders.Network.BUILDER_INTEGER.handle(\n                        network -> network != null ? network.getElements().size() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"elementcount\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_ENERGY_BATTERY_COUNT =\n                    AspectReadBuilders.Network.BUILDER_INTEGER.handle(\n                        network -> network != null ? network.getCapability(Capabilities.EnergyNetwork.NETWORK)\n                                .map(energyNetwork -> energyNetwork.getPrioritizedPositions().size())\n                                .orElse(0): 0\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"energy\").appendKind(\"batterycount\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_ENERGY_STORED =\n                    AspectReadBuilders.Network.ENERGY_BUILDER_LONG.handle(\n                        storage -> storage != null ? storage.getAmountAsLong() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"energy\").appendKind(\"stored\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_ENERGY_MAX =\n                    AspectReadBuilders.Network.ENERGY_BUILDER_LONG.handle(\n                        storage -> storage != null ? storage.getCapacityAsLong() : 0\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"energy\").appendKind(\"max\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_ENERGY_CONSUMPTION_RATE =\n                    AspectReadBuilders.Network.BUILDER_INTEGER.handle(\n                            network -> network != null && GeneralConfig.energyConsumptionMultiplier > 0\n                                    ? network.getElements().stream()\n                                    .mapToInt((e) -> e instanceof IEnergyConsumingNetworkElement\n                                            ? ((IEnergyConsumingNetworkElement) e).getConsumptionRate() : 0).sum()\n                                    * GeneralConfig.energyConsumptionMultiplier : 0\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"energy\").appendKind(\"consumptionrate\").buildRead();\n            public static final IAspectRead<IValue, ValueTypeCategoryAny> ANY_VALUE =\n                    AspectReadBuilders.BUILDER_ANY.appendKind(\"network\").handle(\n                            data -> {\n                                PartPos target = data.getLeft().getTarget();\n                                IValueInterface valueInterface = IModHelpersNeoForge.get().getCapabilityHelpers()\n                                        .getCapability(target.getPos(), target.getSide(), Capabilities.ValueInterface.BLOCK)\n                                        .orElseThrow(() -> {\n                                            EvaluationException error = new EvaluationException(Component.translatable(\n                                                    L10NValues.ASPECT_ERROR_NOVALUEINTERFACE));\n                                            error.setRetryEvaluation(true);\n                                            return error;\n                                        });\n                                return valueInterface.getValue()\n                                        .orElseThrow(() -> new EvaluationException(Component.translatable(\n                                                L10NValues.ASPECT_ERROR_NOVALUEINTERFACEVALUE)));\n                            }\n                    ).appendKind(\"value\").buildRead();\n            public static final IAspectRead<ValueTypeOperator.ValueOperator, ValueTypeOperator>\n                    OPERATOR_GETVARIABLEBYID = AspectReadBuilders.BUILDER_OPERATOR\n                    .appendKind(\"network\")\n                    .handle(input -> ValueTypeOperator.ValueOperator.of(new PositionedOperatorNetworkVariableById(\n                            input.getLeft().getTarget().getPos(),\n                            input.getLeft().getTarget().getSide()\n                    )))\n                    .appendKind(\"variablebyid\")\n                    .buildRead();\n            static {\n                Operators.REGISTRY.registerSerializer(new PositionedOperator.Serializer(\n                        PositionedOperatorNetworkVariableById.class, Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"positioned_network_variable_by_id\")));\n            }\n\n        }\n\n        public static final class Redstone {\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_LOW =\n                    AspectReadBuilders.Redstone.BUILDER_BOOLEAN.handle(\n                        input -> input == 0\n                    ).withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"low\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_NONLOW =\n                    AspectReadBuilders.Redstone.BUILDER_BOOLEAN.handle(\n                        input -> input > 0\n                    ).withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"nonlow\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_HIGH =\n                    AspectReadBuilders.Redstone.BUILDER_BOOLEAN.handle(\n                        input -> input == 15\n                    ).withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"high\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_CLOCK =\n                    AspectReadBuilders.Redstone.BUILDER_BOOLEAN_CLOCK.handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"clock\").buildRead();\n\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_VALUE =\n                    AspectReadBuilders.Redstone.BUILDER_INTEGER.withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"value\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_COMPARATOR =\n                    AspectReadBuilders.Redstone.BUILDER_INTEGER_COMPARATOR.withUpdateType(AspectUpdateType.BLOCK_UPDATE)\n                            .handle(AspectReadBuilders.PROP_GET_INTEGER, \"comparator\").buildRead();\n\n        }\n\n        public static final class World {\n\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_WEATHER_CLEAR =\n                    AspectReadBuilders.World.BUILDER_BOOLEAN.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        world -> !world.isRaining()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"weather\").appendKind(\"clear\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_WEATHER_RAINING =\n                    AspectReadBuilders.World.BUILDER_BOOLEAN.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        net.minecraft.world.level.Level::isRaining\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"weather\").appendKind(\"raining\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_WEATHER_THUNDER =\n                    AspectReadBuilders.World.BUILDER_BOOLEAN.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        net.minecraft.world.level.Level::isThundering\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"weather\").appendKind(\"thunder\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISDAY =\n                    AspectReadBuilders.World.BUILDER_BOOLEAN.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                            net.minecraft.world.level.Level::isBrightOutside\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isday\").buildRead();\n            public static final IAspectRead<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_ISNIGHT =\n                    AspectReadBuilders.World.BUILDER_BOOLEAN.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        world -> !world.isBrightOutside()\n                    ).handle(AspectReadBuilders.PROP_GET_BOOLEAN, \"isnight\").buildRead();\n\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_RAINCOUNTDOWN =\n                    AspectReadBuilders.World.BUILDER_INTEGER.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        world -> ((net.minecraft.server.level.ServerLevel) world).getWeatherData().getRainTime()\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"raincountdown\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_TICKTIME =\n                    AspectReadBuilders.World.BUILDER_INTEGER.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        world -> (int) (Helpers.mean(ServerLifecycleHooks.getCurrentServer().getTickTime(world.dimension())) * 1.0E-6D)\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"ticktime\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_DAYTIME =\n                    AspectReadBuilders.World.BUILDER_INTEGER.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        world -> (int) (world.getOverworldClockTime() % IModHelpers.get().getMinecraftHelpers().getDayLength())\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"daytime\").buildRead();\n            public static final IAspectRead<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_LIGHTLEVEL =\n                    AspectReadBuilders.World.BUILDER_INTEGER.handle(\n                        dimPos -> dimPos.getLevel(true).getMaxLocalRawBrightness(dimPos.getBlockPos())\n                    ).handle(AspectReadBuilders.PROP_GET_INTEGER, \"lightlevel\").buildRead();\n\n            public static final IAspectRead<ValueTypeDouble.ValueDouble, ValueTypeDouble> DOUBLE_TPS =\n                    AspectReadBuilders.World.BUILDER_DOUBLE.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                            world -> Helpers.calculateTps(ServerLifecycleHooks.getCurrentServer().getTickTime(world.dimension()))\n                    ).handle(AspectReadBuilders.PROP_GET_DOUBLE, \"tps\").buildRead();\n\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_TIME =\n                    AspectReadBuilders.World.BUILDER_LONG.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        net.minecraft.world.level.Level::getOverworldClockTime\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"time\").buildRead();\n            public static final IAspectRead<ValueTypeLong.ValueLong, ValueTypeLong> LONG_TOTALTIME =\n                    AspectReadBuilders.World.BUILDER_LONG.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        net.minecraft.world.level.Level::getGameTime\n                    ).handle(AspectReadBuilders.PROP_GET_LONG, \"totaltime\").buildRead();\n\n            public static final IAspectRead<ValueTypeString.ValueString, ValueTypeString> STRING_NAME =\n                    AspectReadBuilders.World.BUILDER_STRING.handle(AspectReadBuilders.World.PROP_GET_WORLD).handle(\n                        world -> ((ServerLevelData) world.getLevelData()).getLevelName()\n                    ).handle(AspectReadBuilders.PROP_GET_STRING, \"worldname\").buildRead();\n\n            public static final IAspectRead<ValueTypeList.ValueList, ValueTypeList> LIST_PLAYERS =\n                    AspectReadBuilders.World.BUILDER_LIST.handle(dimPos ->\n                            ValueTypeList.ValueList.ofList(ValueTypes.OBJECT_ENTITY, Lists.transform(dimPos.getLevel(true).players(),\n                                ValueObjectTypeEntity.ValueEntity::of))\n                    ).appendKind(\"players\").buildRead();\n\n        }\n\n    }\n\n    public static final class Write {\n\n        public static final class Audio {\n\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_HARP_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.HARP), \"harp\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BASEDRUM_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.BASEDRUM), \"basedrum\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_SNARE_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.SNARE), \"snare\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_HAT_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.HAT), \"hat\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BASS_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.BASS), \"bass\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_FLUTE_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.FLUTE), \"flute\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BELL_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.BELL), \"bell\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_GUITAR_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.BASS), \"guitar\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_CHIME_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.CHIME), \"chime\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_XYLOPHONE_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.XYLOPHONE), \"xylophone\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_IRON_XYLOPHONE_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.IRON_XYLOPHONE), \"iron_xylophone\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_COW_BELL_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.COW_BELL), \"cow_bell\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_DIDGERIDOO_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.DIDGERIDOO), \"didgeridoo\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BIT_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.BIT), \"bit\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_BANJO_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.BANJO), \"banjo\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_PLING_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.PLING), \"pling\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_ZOMBIE_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.ZOMBIE), \"zombie\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_SKELETON_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.SKELETON), \"skeleton\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_CREEPER_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.CREEPER), \"creeper\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_DRAGON_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.DRAGON), \"dragon\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_WITHER_SKELETON_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.WITHER_SKELETON), \"wither_skeleton\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_PIGLIN_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.PIGLIN), \"piglin\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_CUSTOM_HEAD_NOTE =\n                    AspectWriteBuilders.Audio.BUILDER_INTEGER_INSTRUMENT\n                            .handle(AspectWriteBuilders.Audio.propWithInstrument(NoteBlockInstrument.CUSTOM_HEAD), \"custom_head\")\n                            .handle(AspectWriteBuilders.Audio.PROP_SET).buildWrite();\n\n            public static final IAspectWrite<ValueTypeString.ValueString, ValueTypeString> STRING_SOUND =\n                    AspectWriteBuilders.Audio.BUILDER_STRING.withProperties(AspectWriteBuilders.Audio.PROPERTIES_SOUND)\n                            .handle(input -> {\n                                IAspectProperties properties = input.getMiddle();\n                                BlockPos pos = input.getLeft().getTarget().getPos().getBlockPos();\n                                if(!StringUtil.isNullOrEmpty(input.getRight())) {\n                                    float f = (float) properties.getValue(AspectWriteBuilders.Audio.PROP_FREQUENCY).getRawValue();\n                                    float volume = (float) properties.getValue(AspectWriteBuilders.Audio.PROP_VOLUME).getRawValue();\n\n                                    Level world = input.getLeft().getTarget().getPos().getLevel(false);\n                                    if (world != null) {\n                                        ServerLifecycleHooks.getCurrentServer().getPlayerList().broadcast(null,\n                                                (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, 64.0D,\n                                                world.dimension(),\n                                                new ClientboundSoundPacket(Holder.direct(SoundEvent.createVariableRangeEvent(ValueHelpers.createIdentifierInEvaluation(input.getRight()))),\n                                                        SoundSource.RECORDS,\n                                                        (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D,\n                                                        volume, f, world.getRandom().nextLong()));\n                                    }\n                                }\n                                return null;\n                            }, \"sound\").buildWrite();\n\n            public static final IAspectWrite<ValueTypeString.ValueString, ValueTypeString> STRING_TEXT =\n                    AspectWriteBuilders.Audio.BUILDER_STRING.withProperties(AspectWriteBuilders.Audio.PROPERTIES_TEXT)\n                            .handle(input -> {\n                                IAspectProperties properties = input.getMiddle();\n                                Level world = input.getLeft().getTarget().getPos().getLevel(true);\n                                BlockPos pos = input.getLeft().getTarget().getPos().getBlockPos();\n                                if(!StringUtil.isNullOrEmpty(input.getRight())) {\n                                    int range = properties.getValue(AspectWriteBuilders.Audio.PROP_RANGE).getRawValue();\n                                    IntegratedDynamics._instance.getPacketHandler().sendToAllAroundPoint(\n                                            new SpeakTextPacket(input.getRight(), (float) properties.getValue(AspectWriteBuilders.Audio.PROP_VOLUME).getRawValue()),\n                                            IPacketHandler.createTargetPointFromLocation((ServerLevel) world, pos, range));\n                                }\n                                return null;\n                            }, \"text\").buildWrite();\n\n        }\n\n        public static final class Effect {\n\n            public static IAspectWrite<ValueTypeDouble.ValueDouble, ValueTypeDouble> createForParticleSpell(final ParticleType<SpellParticleOption> type) {\n                // TODO: expose these params as aspect settings?\n                return createForParticle(SpellParticleOption.create(type, 0, 1));\n            }\n\n            public static IAspectWrite<ValueTypeDouble.ValueDouble, ValueTypeDouble> createForParticlePower(final ParticleType<PowerParticleOption> type) {\n                // TODO: expose these params as aspect settings?\n                return createForParticle(PowerParticleOption.create(type, 1));\n            }\n\n            public static IAspectWrite<ValueTypeDouble.ValueDouble, ValueTypeDouble> createForParticleColor(final ParticleType<ColorParticleOption> type) {\n                // TODO: expose these params as aspect settings?\n                return createForParticle(ColorParticleOption.create(type, 0));\n            }\n\n            public static IAspectWrite<ValueTypeDouble.ValueDouble, ValueTypeDouble> createForParticle(final ParticleOptions particle) {\n                return AspectWriteBuilders.Effect.BUILDER_DOUBLE_PARTICLE.appendKind(\"particle\").appendKind(BuiltInRegistries\n                                .PARTICLE_TYPE.getKey(particle.getType()).toString().toLowerCase(Locale.ROOT).replaceAll(\":\", \"_\"))\n                        .handle(input -> {\n                            double velocity = input.getRight();\n                            if (velocity < 0) {\n                                return null;\n                            }\n\n                            IAspectProperties properties = input.getMiddle();\n                            PartPos pos = input.getLeft().getTarget();\n\n                            double x = pos.getPos().getBlockPos().getX() + properties.getValue(AspectWriteBuilders.Effect.PROP_OFFSET_X).getRawValue();\n                            double y = pos.getPos().getBlockPos().getY() + properties.getValue(AspectWriteBuilders.Effect.PROP_OFFSET_Y).getRawValue();\n                            double z = pos.getPos().getBlockPos().getZ() + properties.getValue(AspectWriteBuilders.Effect.PROP_OFFSET_Z).getRawValue();\n                            int numberOfParticles = properties.getValue(AspectWriteBuilders.Effect.PROP_PARTICLES).getRawValue();\n\n                            double xDir = properties.getValue(AspectWriteBuilders.Effect.PROP_SPREAD_X).getRawValue();\n                            double yDir = properties.getValue(AspectWriteBuilders.Effect.PROP_SPREAD_Y).getRawValue();\n                            double zDir = properties.getValue(AspectWriteBuilders.Effect.PROP_SPREAD_Z).getRawValue();\n\n                            ServerLevel world = ((ServerLevel) pos.getPos().getLevel(false));\n                            if (world != null) {\n                                world.sendParticles(particle, x, y, z, numberOfParticles, xDir, yDir, zDir, velocity);\n                            }\n                            return null;\n                        }).buildWrite();\n            }\n\n        }\n\n        public static final class Redstone {\n\n            public static final IAspectWrite<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN =\n                    AspectWriteBuilders.Redstone.BUILDER_BOOLEAN.handle(\n                        input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight() ? 15 : 0)\n                    ).handle(AspectWriteBuilders.Redstone.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER =\n                    AspectWriteBuilders.Redstone.BUILDER_INTEGER.handle(AspectWriteBuilders.Redstone.PROP_SET).buildWrite();\n            public static final IAspectWrite<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean> BOOLEAN_PULSE =\n                    AspectWriteBuilders.Redstone.BUILDER_BOOLEAN\n                            .withProperties(AspectWriteBuilders.Redstone.PROPERTIES_REDSTONE_PULSE)\n                            .appendKind(\"pulse\")\n                            .handle(input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight() ? 15 : 0)\n                    ).handle(AspectWriteBuilders.Redstone.PROP_SET_PULSE).buildWrite();\n            public static final IAspectWrite<ValueTypeInteger.ValueInteger, ValueTypeInteger> INTEGER_PULSE =\n                    AspectWriteBuilders.Redstone.BUILDER_INTEGER\n                            .withProperties(AspectWriteBuilders.Redstone.PROPERTIES_REDSTONE_PULSE)\n                            .appendKind(\"pulse\")\n                            .handle(AspectWriteBuilders.Redstone.PROP_SET_PULSE).buildWrite();\n\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/read/AspectReadBase.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.read;\n\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.AspectUpdateType;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectRead;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectVariable;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.read.IPartStateReader;\nimport org.cyclops.integrateddynamics.api.part.read.IPartTypeReader;\nimport org.cyclops.integrateddynamics.core.part.aspect.LazyAspectVariable;\nimport org.cyclops.integrateddynamics.part.aspect.AspectBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\nimport java.util.function.Supplier;\n\n/**\n * Base class for read aspects.\n * @author rubensworks\n */\npublic abstract class AspectReadBase<V extends IValue, T extends IValueType<V>> extends AspectBase<V, T>\n        implements IAspectRead<V, T> {\n\n    private final String unlocalizedTypeSuffix;\n    private final AspectUpdateType updateType;\n\n    public AspectReadBase(String modId, String unlocalizedTypeSuffix,\n                          IAspectProperties defaultProperties, AspectUpdateType updateType) {\n        super(modId, defaultProperties);\n        if(unlocalizedTypeSuffix == null) {\n            unlocalizedTypeSuffix = \"\";\n        }\n        this.unlocalizedTypeSuffix = unlocalizedTypeSuffix;\n        this.updateType = updateType;\n        if(IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            registerModelIdentifier();\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void update(INetwork network, IPartNetwork partNetwork, P partType, PartTarget target, S state) {\n        IAspectVariable variable = ((IPartTypeReader) partType).getVariable(target, (IPartStateReader) state, this);\n        variable.invalidate();\n    }\n\n    protected String getUnlocalizedType() {\n        return \"read\" + this.unlocalizedTypeSuffix;\n    }\n\n    protected void registerModelIdentifier() {\n        Aspects.REGISTRY.getClient().registerAspectModel(this,\n                Identifier.parse(getModId() + \":aspect/\" + getUnlocalizedType().replaceAll(\"\\\\.\", \"/\")));\n    }\n\n    /**\n     * This is only called lazy.\n     * @param target The target to get the value for.\n     * @param properties The optional properties for this aspect.\n     * @return The value that will be inserted into a variable so it can be used elsewhere.\n     * @throws EvaluationException If evaluation has gone wrong.\n     */\n    protected abstract V getValue(PartTarget target, IAspectProperties properties) throws EvaluationException;\n\n    @Override\n    public IAspectVariable<V> createNewVariable(Supplier<PartTarget> target) {\n        return new LazyAspectVariable<V>(getValueType(), target, this) {\n            @Override\n            public V getValueLazy() throws EvaluationException {\n                return AspectReadBase.this.getValue(getTarget(), getAspectProperties());\n            }\n        };\n    }\n\n    @Override\n    public AspectUpdateType getUpdateType() {\n        return updateType;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/read/AspectReadBuilders.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.read;\n\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.server.MinecraftServer;\nimport net.minecraft.world.entity.decoration.ItemFrame;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.RedStoneWireBlock;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.NoteBlockInstrument;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.Vec3;\nimport net.neoforged.neoforge.event.level.NoteBlockEvent;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport net.neoforged.neoforge.server.ServerLifecycleHooks;\nimport net.neoforged.neoforge.transfer.EmptyResourceHandler;\nimport net.neoforged.neoforge.transfer.ResourceHandler;\nimport net.neoforged.neoforge.transfer.energy.EnergyHandler;\nimport net.neoforged.neoforge.transfer.fluid.FluidResource;\nimport net.neoforged.neoforge.transfer.item.ItemResource;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.commoncapabilities.api.capability.Capabilities;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;\nimport org.cyclops.commoncapabilities.api.capability.temperature.ITemperature;\nimport org.cyclops.commoncapabilities.api.capability.work.IWorker;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;\nimport org.cyclops.integrateddynamics.api.part.PartPos;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.core.NoteBlockEventReceiver;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.helper.NetworkHelpers;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.AspectBuilder;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.IAspectValuePropagator;\nimport org.cyclops.integrateddynamics.core.part.aspect.property.AspectProperties;\nimport org.cyclops.integrateddynamics.core.part.aspect.property.AspectPropertyTypeInstance;\n\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\n/**\n * Collection of aspect read builders and value propagators.\n * @author rubensworks\n */\npublic class AspectReadBuilders {\n\n    // --------------- Value type builders ---------------\n    public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, Pair<PartTarget, IAspectProperties>>\n            BUILDER_BOOLEAN = AspectBuilder.forReadType(ValueTypes.BOOLEAN);\n    public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Pair<PartTarget, IAspectProperties>>\n            BUILDER_INTEGER = AspectBuilder.forReadType(ValueTypes.INTEGER);\n    public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, Pair<PartTarget, IAspectProperties>>\n            BUILDER_DOUBLE = AspectBuilder.forReadType(ValueTypes.DOUBLE);\n    public static final AspectBuilder<ValueTypeLong.ValueLong, ValueTypeLong, Pair<PartTarget, IAspectProperties>>\n            BUILDER_LONG = AspectBuilder.forReadType(ValueTypes.LONG);\n    public static final AspectBuilder<ValueTypeString.ValueString, ValueTypeString, Pair<PartTarget, IAspectProperties>>\n            BUILDER_STRING = AspectBuilder.forReadType(ValueTypes.STRING);\n    public static final AspectBuilder<ValueObjectTypeEntity.ValueEntity, ValueObjectTypeEntity, Pair<PartTarget, IAspectProperties>>\n            BUILDER_ENTITY = AspectBuilder.forReadType(ValueTypes.OBJECT_ENTITY);\n    public static final AspectBuilder<ValueTypeList.ValueList, ValueTypeList, Pair<PartTarget, IAspectProperties>>\n            BUILDER_LIST = AspectBuilder.forReadType(ValueTypes.LIST);\n    public static final AspectBuilder<ValueTypeNbt.ValueNbt, ValueTypeNbt, Pair<PartTarget, IAspectProperties>>\n            BUILDER_NBT = AspectBuilder.forReadType(ValueTypes.NBT);\n    public static final AspectBuilder<IValue, ValueTypeCategoryAny, Pair<PartTarget, IAspectProperties>>\n            BUILDER_ANY = AspectBuilder.forReadType(ValueTypes.CATEGORY_ANY);\n    public static final AspectBuilder<ValueTypeOperator.ValueOperator, ValueTypeOperator, Pair<PartTarget, IAspectProperties>>\n            BUILDER_OPERATOR = AspectBuilder.forReadType(ValueTypes.OPERATOR);\n\n    public static final AspectBuilder<ValueObjectTypeItemStack.ValueItemStack, ValueObjectTypeItemStack, Pair<PartTarget, IAspectProperties>>\n            BUILDER_OBJECT_ITEMSTACK = AspectBuilder.forReadType(ValueTypes.OBJECT_ITEMSTACK);\n    public static final AspectBuilder<ValueObjectTypeBlock.ValueBlock, ValueObjectTypeBlock, Pair<PartTarget, IAspectProperties>>\n            BUILDER_OBJECT_BLOCK = AspectBuilder.forReadType(ValueTypes.OBJECT_BLOCK);\n    public static final AspectBuilder<ValueObjectTypeFluidStack.ValueFluidStack, ValueObjectTypeFluidStack, Pair<PartTarget, IAspectProperties>>\n            BUILDER_OBJECT_FLUIDSTACK = AspectBuilder.forReadType(ValueTypes.OBJECT_FLUIDSTACK);\n\n    // --------------- Value type propagators ---------------\n    public static final IAspectValuePropagator<Boolean, ValueTypeBoolean.ValueBoolean>\n        PROP_GET_BOOLEAN = ValueTypeBoolean.ValueBoolean::of;\n\n    public static final IAspectValuePropagator<Integer, ValueTypeInteger.ValueInteger>\n        PROP_GET_INTEGER = ValueTypeInteger.ValueInteger::of;\n\n    public static final IAspectValuePropagator<Double, ValueTypeDouble.ValueDouble>\n        PROP_GET_DOUBLE = ValueTypeDouble.ValueDouble::of;\n\n    public static final IAspectValuePropagator<Long, ValueTypeLong.ValueLong>\n        PROP_GET_LONG = ValueTypeLong.ValueLong::of;\n\n    public static final IAspectValuePropagator<ItemStack, ValueObjectTypeItemStack.ValueItemStack>\n        PROP_GET_ITEMSTACK = ValueObjectTypeItemStack.ValueItemStack::of;\n\n    public static final IAspectValuePropagator<String, ValueTypeString.ValueString>\n        PROP_GET_STRING = ValueTypeString.ValueString::of;\n\n    public static final IAspectValuePropagator<BlockState, ValueObjectTypeBlock.ValueBlock>\n        PROP_GET_BLOCK = ValueObjectTypeBlock.ValueBlock::of;\n\n    public static final IAspectValuePropagator<FluidStack, ValueObjectTypeFluidStack.ValueFluidStack>\n        PROP_GET_FLUIDSTACK = ValueObjectTypeFluidStack.ValueFluidStack::of;\n\n    public static final IAspectValuePropagator<Optional<Tag>,ValueTypeNbt.ValueNbt>\n        PROP_GET_NBT = ValueTypeNbt.ValueNbt::of;\n\n    // --------------- Value type validators ---------------\n    public static final Predicate<ValueTypeInteger.ValueInteger>\n        VALIDATOR_INTEGER_POSITIVE = input -> input.getRawValue() >= 0;\n\n    public static final Predicate<ValueTypeDouble.ValueDouble>\n        VALIDATOR_DOUBLE_POSITIVE = input -> input.getRawValue() >= 0;\n\n    // --------------- Generic properties ---------------\n    public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_LISTINDEX =\n            new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.listindex\", VALIDATOR_INTEGER_POSITIVE);\n    public static final IAspectProperties LIST_PROPERTIES = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n            PROPERTY_LISTINDEX\n    ));\n    static {\n        LIST_PROPERTIES.setValue(PROPERTY_LISTINDEX, ValueTypeInteger.ValueInteger.of(0));\n    }\n\n    public static final class Audio {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_RANGE =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.range\", VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectProperties NOTE_PROPERTIES = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROPERTY_RANGE\n        ));\n        static {\n            NOTE_PROPERTIES.setValue(PROPERTY_RANGE, ValueTypeInteger.ValueInteger.of(64));\n        }\n\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Pair<PartTarget, IAspectProperties>>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.appendKind(\"audio\");\n\n        public static AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Integer> forInstrument(final NoteBlockInstrument instrument) {\n            return BUILDER_INTEGER.appendKind(\"instrument\").handle(input -> {\n                for (NoteBlockEvent.Play event : NoteBlockEventReceiver.getInstance().getEvents().get(instrument)) {\n                    net.minecraft.world.level.Level world = input.getLeft().getTarget().getPos().getLevel(false);\n                    if (world != null) {\n                        BlockPos pos = input.getLeft().getTarget().getPos().getBlockPos();\n                        int range = input.getRight().getValue(PROPERTY_RANGE).getRawValue();\n                        if (world.dimensionType() == event.getLevel().dimensionType()\n                                && pos.distSqr(event.getPos()) <= range * range) {\n                            return event.getVanillaNoteId();\n                        }\n                    }\n                }\n                return -1;\n            }, instrument.name().toLowerCase(Locale.ENGLISH)).withProperties(NOTE_PROPERTIES);\n        }\n\n    }\n\n    public static final class Block {\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, DimPos>\n                PROP_GET = input -> input.getLeft().getTarget().getPos();\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, DimPos>\n                BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET, \"block\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, DimPos>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET, \"block\");\n        public static final AspectBuilder<ValueTypeString.ValueString, ValueTypeString, DimPos>\n                BUILDER_STRING = AspectReadBuilders.BUILDER_STRING.handle(PROP_GET, \"block\");\n        public static final AspectBuilder<ValueObjectTypeBlock.ValueBlock, ValueObjectTypeBlock, DimPos>\n                BUILDER_BLOCK = AspectReadBuilders.BUILDER_OBJECT_BLOCK.handle(PROP_GET, \"block\");\n        public static final AspectBuilder<ValueTypeNbt.ValueNbt, ValueTypeNbt, DimPos>\n                BUILDER_NBT = AspectReadBuilders.BUILDER_NBT.handle(PROP_GET, \"block\");\n\n    }\n\n    public static final class Entity {\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, DimPos>\n                PROP_GET = input -> input.getLeft().getTarget().getPos();\n\n        public static final AspectBuilder<ValueObjectTypeEntity.ValueEntity, ValueObjectTypeEntity, Pair<PartTarget, IAspectProperties>>\n                BUILDER_ENTITY = AspectReadBuilders.BUILDER_ENTITY.appendKind(\"entity\");\n        public static final AspectBuilder<ValueTypeList.ValueList, ValueTypeList, DimPos>\n                BUILDER_LIST = AspectReadBuilders.BUILDER_LIST.handle(PROP_GET, \"entity\");\n        public static final AspectBuilder<ValueObjectTypeItemStack.ValueItemStack, ValueObjectTypeItemStack, Pair<PartTarget, IAspectProperties>>\n                BUILDER_ITEMSTACK = AspectReadBuilders.BUILDER_OBJECT_ITEMSTACK.appendKind(\"entity\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Pair<PartTarget, IAspectProperties>>\n                BUILDER_INTEGER_ALL = AspectReadBuilders.BUILDER_INTEGER.appendKind(\"entity\");\n\n    }\n\n    public static final class ExtraDimensional {\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, MinecraftServer>\n                PROP_GET = input -> ServerLifecycleHooks.getCurrentServer();\n\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, MinecraftServer>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET, \"extradimensional\");\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, MinecraftServer>\n                BUILDER_DOUBLE = AspectReadBuilders.BUILDER_DOUBLE.handle(PROP_GET, \"extradimensional\");\n        public static final AspectBuilder<ValueTypeList.ValueList, ValueTypeList, MinecraftServer>\n                BUILDER_LIST = AspectReadBuilders.BUILDER_LIST.handle(PROP_GET, \"extradimensional\");\n\n    }\n\n    public static final class Fluid {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROP_TANKID =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.tankid\", VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectProperties PROPERTIES = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROP_TANKID\n        ));\n        static {\n            PROPERTIES.setValue(PROP_TANKID, ValueTypeInteger.ValueInteger.of(0)); // Not required in this case, but we do this here just as an example on how to set default values.\n        }\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ResourceHandler<FluidResource>> PROP_GET = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos, input.getLeft().getTarget().getSide(),\n                    net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK)\n                    .orElse(EmptyResourceHandler.instance());\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, Pair<ResourceHandler<FluidResource>, Integer>> PROP_GET_ACTIVATABLE = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            ResourceHandler<FluidResource> fluidHandler = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos, input.getLeft().getTarget().getSide(),\n                    net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK).orElse(null);\n            if(fluidHandler != null) {\n                int i = input.getRight().getValue(PROP_TANKID).getRawValue();\n                if(i < fluidHandler.size()) {\n                    return Pair.of(fluidHandler, i);\n                }\n            }\n            return null;\n        };\n        public static final IAspectValuePropagator<Pair<ResourceHandler<FluidResource>, Integer>, FluidStack>\n                PROP_GET_FLUIDSTACK = tankInfo -> tankInfo != null ? tankInfo.getLeft().getResource(tankInfo.getRight()).toStack(tankInfo.getLeft().getAmountAsInt(tankInfo.getRight())) : FluidStack.EMPTY;\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ValueTypeList.ValueList>\n                PROP_GET_LIST_FLUIDSTACKS = input -> ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyPositionedTankFluidStacks(\n                        input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                ));\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ValueTypeList.ValueList>\n                PROP_GET_LIST_CAPACITIES = input -> ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyPositionedTankCapacities(\n                        input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()\n                ));\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, ResourceHandler<FluidResource>>\n                BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET, \"fluid\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, ResourceHandler<FluidResource>>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET, \"fluid\");\n        public static final AspectBuilder<ValueTypeLong.ValueLong, ValueTypeLong, ResourceHandler<FluidResource>>\n                BUILDER_LONG = AspectReadBuilders.BUILDER_LONG.handle(PROP_GET, \"fluid\");\n        public static final AspectBuilder<ValueTypeLong.ValueLong, ValueTypeLong, Pair<ResourceHandler<FluidResource>, Integer>>\n                BUILDER_LONG_ACTIVATABLE = AspectReadBuilders.BUILDER_LONG.handle(PROP_GET_ACTIVATABLE, \"fluid\").withProperties(PROPERTIES);\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, Pair<ResourceHandler<FluidResource>, Integer>>\n                BUILDER_DOUBLE_ACTIVATABLE = AspectReadBuilders.BUILDER_DOUBLE.handle(PROP_GET_ACTIVATABLE, \"fluid\").withProperties(PROPERTIES);\n\n    }\n\n    public static final class Inventory {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_SLOTID =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.slotid\", VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectProperties PROPERTIES = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROPERTY_SLOTID\n        ));\n        static {\n            PROPERTIES.setValue(PROPERTY_SLOTID, ValueTypeInteger.ValueInteger.of(0)); // Not required in this case, but we do this here just as an example on how to set default values.\n        }\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ResourceHandler<ItemResource>> PROP_GET = input -> {\n            PartPos target = input.getLeft().getTarget();\n            return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(target.getPos().getLevel(true), target.getPos().getBlockPos(), target.getSide(), net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK).orElse(null);\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ItemStack> PROP_GET_SLOT = input -> {\n            PartPos target = input.getLeft().getTarget();\n            ResourceHandler<ItemResource> itemHandler = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(target.getPos().getLevel(true), target.getPos().getBlockPos(), target.getSide(), net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK).orElse(null);\n            int slotId = input.getRight().getValue(PROPERTY_SLOTID).getRawValue();\n            if(itemHandler != null && slotId >= 0 && slotId < itemHandler.size()) {\n                return itemHandler.getResource(slotId).toStack(itemHandler.getAmountAsInt(slotId));\n            }\n            return ItemStack.EMPTY;\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ValueTypeList.ValueList>\n                PROP_GET_LIST = input -> ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyPositionedInventory(input.getLeft().getTarget().getPos(), input.getLeft().getTarget().getSide()));\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, ResourceHandler<ItemResource>>\n                BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET, \"inventory\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, ResourceHandler<ItemResource>>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET, \"inventory\");\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, ResourceHandler<ItemResource>>\n                BUILDER_DOUBLE = AspectReadBuilders.BUILDER_DOUBLE.handle(PROP_GET, \"inventory\");\n        public static final AspectBuilder<ValueObjectTypeItemStack.ValueItemStack, ValueObjectTypeItemStack, ItemStack>\n                BUILDER_ITEMSTACK = BUILDER_OBJECT_ITEMSTACK.handle(PROP_GET_SLOT, \"inventory\").withProperties(PROPERTIES);\n\n    }\n\n    public static final class Machine {\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, IWorker> PROP_GET_WORKER = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos.getLevel(true), dimPos.getBlockPos(), input.getLeft().getTarget().getSide(), Capabilities.Worker.BLOCK).orElse(null);\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ITemperature> PROP_GET_TEMPERATURE = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos.getLevel(true), dimPos.getBlockPos(), input.getLeft().getTarget().getSide(), org.cyclops.commoncapabilities.api.capability.Capabilities.Temperature.BLOCK).orElse(null);\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, IRecipeHandler> PROP_GET_RECIPE_HANDLER = new IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, IRecipeHandler>() {\n            @Override\n            public IRecipeHandler getOutput(Pair<PartTarget, IAspectProperties> input) {\n                DimPos dimPos = input.getLeft().getTarget().getPos();\n                return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos.getLevel(true), dimPos.getBlockPos(), input.getLeft().getTarget().getSide(), org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK).orElse(null);\n            }\n        };\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, IWorker>\n                BUILDER_WORKER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET_WORKER, \"machine\");\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, ITemperature>\n                BUILDER_TEMPERATURE_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET_TEMPERATURE, \"temperature\");\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, IRecipeHandler>\n                BUILDER_RECIPE_HANDLER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET_RECIPE_HANDLER, \"recipehandler\");\n\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, ITemperature>\n                BUILDER_TEMPERATURE_DOUBLE = AspectReadBuilders.BUILDER_DOUBLE.handle(PROP_GET_TEMPERATURE, \"temperature\");\n\n        public static final AspectBuilder<ValueTypeList.ValueList, ValueTypeList, Pair<PartTarget, IAspectProperties>>\n                BUILDER_RECIPE_HANDLER_LIST = AspectReadBuilders.BUILDER_LIST.appendKind(\"recipehandler\");\n        public static final AspectBuilder<ValueTypeOperator.ValueOperator, ValueTypeOperator, Pair<PartTarget, IAspectProperties>>\n                BUILDER_RECIPE_HANDLER_OPERATOR = AspectReadBuilders.BUILDER_OPERATOR.appendKind(\"recipehandler\");\n    }\n\n    public static final class Network {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_CHANNEL =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.channel\");\n        public static final IAspectProperties PROPERTIES = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROPERTY_CHANNEL\n        ));\n        static {\n            PROPERTIES.setValue(PROPERTY_CHANNEL, ValueTypeInteger.ValueInteger.of(IPositionedAddonsNetwork.WILDCARD_CHANNEL));\n        }\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, INetwork> PROP_GET_NETWORK = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            return NetworkHelpers.getNetwork(dimPos.getLevel(true), dimPos.getBlockPos(), input.getLeft().getTarget().getSide()).orElse(null);\n        };\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, INetwork>\n                BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET_NETWORK, \"network\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, INetwork>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET_NETWORK, \"network\");\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, EnergyHandler> PROP_GET_ENERGY_CHANNEL = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            INetwork network = NetworkHelpers.getNetwork(dimPos.getLevel(true), dimPos.getBlockPos(), input.getLeft().getTarget().getSide()).orElse(null);\n            int channel = input.getRight().getValue(PROPERTY_CHANNEL).getRawValue();\n            return network != null ? network.getCapability(org.cyclops.integrateddynamics.Capabilities.EnergyNetwork.NETWORK)\n                    .map(energyNetwork -> energyNetwork.getChannelExternal(net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK, channel))\n                    .orElse(null) : null;\n        };\n\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, EnergyHandler>\n                ENERGY_BUILDER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET_ENERGY_CHANNEL, \"network\").withProperties(PROPERTIES);\n        public static final AspectBuilder<ValueTypeLong.ValueLong, ValueTypeLong, EnergyHandler>\n                ENERGY_BUILDER_LONG = AspectReadBuilders.BUILDER_LONG.handle(PROP_GET_ENERGY_CHANNEL, \"network\").withProperties(PROPERTIES);\n\n    }\n\n    public static final class Redstone {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_INTERVAL =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.interval\", VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_LENGTH =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.length\", VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROPERTY_OFFSET =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.offset\", VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectProperties PROPERTIES_CLOCK = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROPERTY_INTERVAL,\n                PROPERTY_LENGTH,\n                PROPERTY_OFFSET\n        ));\n        static {\n            PROPERTIES_CLOCK.setValue(PROPERTY_INTERVAL, ValueTypeInteger.ValueInteger.of(20));\n            PROPERTIES_CLOCK.setValue(PROPERTY_LENGTH, ValueTypeInteger.ValueInteger.of(1));\n            PROPERTIES_CLOCK.setValue(PROPERTY_OFFSET, ValueTypeInteger.ValueInteger.of(0));\n        }\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, Integer> PROP_GET = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            int power = dimPos.getLevel(true).getSignal(dimPos.getBlockPos(), input.getLeft().getTarget().getSide());\n            if (power == 0) {\n                BlockState targetBlockState = dimPos.getLevel(true).getBlockState(dimPos.getBlockPos());\n                power = targetBlockState.getBlock() == Blocks.REDSTONE_WIRE ? targetBlockState.getValue(RedStoneWireBlock.POWER) : 0;\n            }\n            return power;\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, Integer> PROP_GET_COMPARATOR = input -> {\n            DimPos dimPos = input.getLeft().getTarget().getPos();\n            BlockState blockState = dimPos.getLevel(true).getBlockState(dimPos.getBlockPos());\n            return blockState.hasAnalogOutputSignal()\n                    ? blockState.getAnalogOutputSignal(dimPos.getLevel(true), dimPos.getBlockPos(), input.getLeft().getTarget().getSide()) : 0;\n        };\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, Boolean> PROP_GET_CLOCK = input -> {\n            int interval = Math.max(1, input.getRight().getValue(PROPERTY_INTERVAL).getRawValue());\n            int length = Math.max(1, input.getRight().getValue(PROPERTY_LENGTH).getRawValue());\n            int offset = input.getRight().getValue(PROPERTY_OFFSET).getRawValue();\n            return (input.getLeft().getTarget().getPos().getLevel(true).getGameTime() - offset) % interval < length;\n        };\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, Integer>\n                BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET, \"redstone\");\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, Boolean>\n                BUILDER_BOOLEAN_CLOCK = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET_CLOCK, \"redstone\")\n                    .withProperties(PROPERTIES_CLOCK);\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Integer>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET, \"redstone\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Integer>\n                BUILDER_INTEGER_COMPARATOR = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET_COMPARATOR, \"redstone\");\n\n    }\n\n    public static final class World {\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, DimPos>\n                PROP_GET = input -> input.getLeft().getTarget().getPos();\n\n        public static final IAspectValuePropagator<DimPos, net.minecraft.world.level.Level>\n                PROP_GET_WORLD = dimPos -> dimPos.getLevel(true);\n\n        public static final IAspectValuePropagator<DimPos, BlockPos>\n                PROP_GET_POS = DimPos::getBlockPos;\n\n        private static final com.google.common.base.Predicate<net.minecraft.world.entity.Entity>\n                ENTITY_SELECTOR_ITEMFRAME = entity -> entity instanceof ItemFrame;\n\n        public static final IAspectValuePropagator<Pair<PartTarget, IAspectProperties>, ItemFrame> PROP_GET_ITEMFRAME = pair -> {\n            DimPos dimPos = pair.getLeft().getTarget().getPos();\n            Direction facing = pair.getLeft().getTarget().getSide();\n            List<net.minecraft.world.entity.Entity> entities = dimPos.getLevel(true).getEntities((net.minecraft.world.entity.Entity) null,\n                    new AABB(Vec3.atLowerCornerOf(dimPos.getBlockPos()), Vec3.atLowerCornerOf(dimPos.getBlockPos().offset(1, 1, 1))), ENTITY_SELECTOR_ITEMFRAME);\n            for(net.minecraft.world.entity.Entity entity : entities) {\n                if(Direction.fromYRot(((ItemFrame) entity).yRotO) == facing.getOpposite()) {\n                    return ((ItemFrame) entity);\n                }\n            }\n            return null;\n        };\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, DimPos>\n                BUILDER_BOOLEAN = AspectReadBuilders.BUILDER_BOOLEAN.handle(PROP_GET, \"world\");\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, DimPos>\n                BUILDER_INTEGER = AspectReadBuilders.BUILDER_INTEGER.handle(PROP_GET, \"world\");\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, DimPos>\n                BUILDER_DOUBLE = AspectReadBuilders.BUILDER_DOUBLE.handle(PROP_GET, \"world\");\n        public static final AspectBuilder<ValueTypeLong.ValueLong, ValueTypeLong, DimPos>\n                BUILDER_LONG = AspectReadBuilders.BUILDER_LONG.handle(PROP_GET, \"world\");\n        public static final AspectBuilder<ValueTypeString.ValueString, ValueTypeString, DimPos>\n                BUILDER_STRING = AspectReadBuilders.BUILDER_STRING.handle(PROP_GET, \"world\");\n        public static final AspectBuilder<ValueTypeList.ValueList, ValueTypeList, DimPos>\n                BUILDER_LIST = AspectReadBuilders.BUILDER_LIST.handle(PROP_GET, \"world\");\n\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/read/redstone/IReadRedstoneComponent.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.read.redstone;\n\nimport net.minecraft.core.Direction;\nimport java.util.Optional;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * Interface for redstone reading the component.\n * @author rubensworks\n */\npublic interface IReadRedstoneComponent {\n\n    public void setAllowRedstoneInput(PartTarget target, boolean allow);\n\n    public Optional<IDynamicRedstone> getDynamicRedstoneBlock(DimPos dimPos, Direction side);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/read/redstone/ReadRedstoneComponent.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.read.redstone;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\nimport java.util.Optional;\n\n/**\n * Default component for writing redstone levels.\n * @author rubensworks\n */\npublic class ReadRedstoneComponent implements IReadRedstoneComponent {\n    @Override\n    public void setAllowRedstoneInput(PartTarget target, boolean allow) {\n        DimPos dimPos = target.getCenter().getPos();\n        getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                .ifPresent(block -> block.setAllowRedstoneInput(allow));\n    }\n\n    @Override\n    public Optional<IDynamicRedstone> getDynamicRedstoneBlock(DimPos dimPos, Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos, side, Capabilities.DynamicRedstone.BLOCK);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/write/AspectWriteBase.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.write;\n\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.network.INetwork;\nimport org.cyclops.integrateddynamics.api.network.IPartNetwork;\nimport org.cyclops.integrateddynamics.api.part.IPartState;\nimport org.cyclops.integrateddynamics.api.part.IPartType;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;\nimport org.cyclops.integrateddynamics.core.helper.L10NValues;\nimport org.cyclops.integrateddynamics.part.aspect.AspectBase;\nimport org.cyclops.integrateddynamics.part.aspect.Aspects;\n\n/**\n * Base class for write aspects.\n * @author rubensworks\n */\npublic abstract class AspectWriteBase<V extends IValue, T extends IValueType<V>> extends AspectBase<V, T>\n        implements IAspectWrite<V, T> {\n\n    protected final String unlocalizedTypeSuffix;\n\n    public AspectWriteBase(String modId, String unlocalizedTypeSuffix, IAspectProperties defaultProperties) {\n        super(modId, defaultProperties);\n        if(unlocalizedTypeSuffix == null) {\n            unlocalizedTypeSuffix = \"\";\n        }\n        this.unlocalizedTypeSuffix = unlocalizedTypeSuffix;\n        if(IModHelpers.get().getMinecraftHelpers().isClientSide()) {\n            registerModelIdentifier();\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void update(INetwork network, IPartNetwork partNetwork, P partType, PartTarget target, S state) {\n        IPartTypeWriter partTypeWriter = (IPartTypeWriter) partType;\n        IPartStateWriter writerState = (IPartStateWriter) state;\n        IVariable variable = partTypeWriter.getActiveVariable(network, partNetwork, target, writerState);\n        if(variable != null && writerState.getErrors(this).isEmpty()) {\n            if (ValueHelpers.correspondsTo(variable, getValueType())) {\n                if(writerState.isDeactivated() || writerState.checkAndResetFirstTick()) {\n                    onActivate(partTypeWriter, target, writerState);\n                }\n                try {\n                    write(partTypeWriter, target, writerState, variable);\n                } catch (EvaluationException e) {\n                    writerState.addError(this, e.getErrorMessage());\n                    writerState.setDeactivated(true);\n                    e.addResolutionListeners(() -> writerState.onVariableContentsUpdated(partTypeWriter, target));\n                }\n            } else {\n                // This will only occur in cases where the variable is of any type, and the value type is more precise.\n                // In all other cases, type checking will already have happen using the variable facades.\n                try {\n                    writerState.addError(this, Component.translatable(L10NValues.ASPECT_ERROR_INVALIDTYPE,\n                        Component.translatable(getValueType().getTranslationKey()),\n                        Component.translatable(variable.getValue().getType().getTranslationKey())));\n                } catch (EvaluationException e) {\n                    // Fallback to a less precise form of error reporting\n                    writerState.addError(this, Component.translatable(L10NValues.ASPECT_ERROR_INVALIDTYPE,\n                            Component.translatable(getValueType().getTranslationKey()),\n                            Component.translatable(variable.getType().getTranslationKey())));\n                }\n                writerState.setDeactivated(true);\n            }\n        } else if(!writerState.isDeactivated()) {\n            onDeactivate(partTypeWriter, target, writerState);\n        }\n    }\n\n    @Override\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onActivate(P partType, PartTarget target, S state) {\n        state.setDeactivated(false);\n    }\n\n    @Override\n    public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onDeactivate(P partType, PartTarget target, S state) {\n        state.setDeactivated(true);\n    }\n\n    @Override\n    public <P extends IPartType<P, S>, S extends IPartState<P>> void setProperties(P partType, PartTarget target, S state, IAspectProperties properties) {\n        onDeactivate((IPartTypeWriter) partType, target, (IPartStateWriter) state);\n        super.setProperties(partType, target, state, properties);\n        onActivate((IPartTypeWriter) partType, target, (IPartStateWriter) state);\n    }\n\n    protected String getUnlocalizedType() {\n        return \"write\" + unlocalizedTypeSuffix;\n    }\n\n    protected void registerModelIdentifier() {\n        Aspects.REGISTRY.getClient().registerAspectModel(this,\n                Identifier.parse(getModId() + \":aspect/\" + getUnlocalizedType().replaceAll(\"\\\\.\", \"/\")));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/write/AspectWriteBuilders.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.write;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Sets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.sounds.SoundSource;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.NoteBlockInstrument;\nimport net.neoforged.neoforge.event.level.NoteBlockEvent;\nimport net.neoforged.neoforge.fluids.FluidStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties;\nimport org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.api.part.write.IPartStateWriter;\nimport org.cyclops.integrateddynamics.api.part.write.IPartTypeWriter;\nimport org.cyclops.integrateddynamics.core.evaluate.variable.*;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.AspectBuilder;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.IAspectValuePropagator;\nimport org.cyclops.integrateddynamics.core.part.aspect.build.IAspectWriteDeactivator;\nimport org.cyclops.integrateddynamics.core.part.aspect.property.AspectProperties;\nimport org.cyclops.integrateddynamics.core.part.aspect.property.AspectPropertyTypeInstance;\nimport org.cyclops.integrateddynamics.part.aspect.read.AspectReadBuilders;\nimport org.cyclops.integrateddynamics.part.aspect.write.redstone.IWriteRedstoneComponent;\nimport org.cyclops.integrateddynamics.part.aspect.write.redstone.WriteRedstoneComponent;\n\nimport java.util.Optional;\n\n/**\n * Collection of aspect write builders and value propagators.\n * @author rubensworks\n */\npublic class AspectWriteBuilders {\n\n    // --------------- Value type builders ---------------\n    public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, Triple<PartTarget, IAspectProperties, ValueTypeBoolean.ValueBoolean>>\n            BUILDER_BOOLEAN = getValue(AspectBuilder.forWriteType(ValueTypes.BOOLEAN));\n    public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Triple<PartTarget, IAspectProperties, ValueTypeInteger.ValueInteger>>\n            BUILDER_INTEGER = getValue(AspectBuilder.forWriteType(ValueTypes.INTEGER));\n    public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, Triple<PartTarget, IAspectProperties, ValueTypeDouble.ValueDouble>>\n            BUILDER_DOUBLE = getValue(AspectBuilder.forWriteType(ValueTypes.DOUBLE));\n    public static final AspectBuilder<ValueTypeString.ValueString, ValueTypeString, Triple<PartTarget, IAspectProperties, ValueTypeString.ValueString>>\n            BUILDER_STRING = getValue(AspectBuilder.forWriteType(ValueTypes.STRING));\n    public static final AspectBuilder<ValueTypeList.ValueList, ValueTypeList, Triple<PartTarget, IAspectProperties, ValueTypeList.ValueList>>\n            BUILDER_LIST = getValue(AspectBuilder.forWriteType(ValueTypes.LIST));\n    public static final AspectBuilder<ValueObjectTypeItemStack.ValueItemStack, ValueObjectTypeItemStack, Triple<PartTarget, IAspectProperties, ValueObjectTypeItemStack.ValueItemStack>>\n            BUILDER_ITEMSTACK = getValue(AspectBuilder.forWriteType(ValueTypes.OBJECT_ITEMSTACK));\n    public static final AspectBuilder<ValueObjectTypeFluidStack.ValueFluidStack, ValueObjectTypeFluidStack, Triple<PartTarget, IAspectProperties, ValueObjectTypeFluidStack.ValueFluidStack>>\n            BUILDER_FLUIDSTACK = getValue(AspectBuilder.forWriteType(ValueTypes.OBJECT_FLUIDSTACK));\n    public static final AspectBuilder<ValueTypeOperator.ValueOperator, ValueTypeOperator, Triple<PartTarget, IAspectProperties, ValueTypeOperator.ValueOperator>>\n            BUILDER_OPERATOR = getValue(AspectBuilder.forWriteType(ValueTypes.OPERATOR));\n    public static final AspectBuilder<ValueTypeNbt.ValueNbt, ValueTypeNbt, Triple<PartTarget, IAspectProperties, ValueTypeNbt.ValueNbt>>\n            BUILDER_NBT = getValue(AspectBuilder.forWriteType(ValueTypes.NBT));\n    public static final AspectBuilder<ValueObjectTypeRecipe.ValueRecipe, ValueObjectTypeRecipe, Triple<PartTarget, IAspectProperties, ValueObjectTypeRecipe.ValueRecipe>>\n            BUILDER_RECIPE = getValue(AspectBuilder.forWriteType(ValueTypes.OBJECT_RECIPE));\n\n    // --------------- Value type propagators ---------------\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueTypeBoolean.ValueBoolean>, Triple<PartTarget, IAspectProperties, Boolean>>\n            PROP_GET_BOOLEAN = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties,ValueTypeInteger.ValueInteger>, Triple<PartTarget, IAspectProperties, Integer>>\n            PROP_GET_INTEGER = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueTypeDouble.ValueDouble>, Triple<PartTarget, IAspectProperties, Double>>\n            PROP_GET_DOUBLE = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueTypeLong.ValueLong>, Triple<PartTarget, IAspectProperties, Long>>\n            PROP_GET_LONG = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueObjectTypeItemStack.ValueItemStack>, Triple<PartTarget, IAspectProperties, ItemStack>>\n            PROP_GET_ITEMSTACK = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueTypeString.ValueString>, Triple<PartTarget, IAspectProperties, String>>\n            PROP_GET_STRING = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueObjectTypeBlock.ValueBlock>, Triple<PartTarget, IAspectProperties, BlockState>>\n            PROP_GET_BLOCK = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue().orElse(null));\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueObjectTypeFluidStack.ValueFluidStack>, Triple<PartTarget, IAspectProperties, FluidStack>>\n            PROP_GET_FLUIDSTACK = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueTypeNbt.ValueNbt>, Triple<PartTarget, IAspectProperties, Optional<Tag>>>\n            PROP_GET_NBT = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue());\n\n    public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, ValueObjectTypeRecipe.ValueRecipe>, Triple<PartTarget, IAspectProperties, IRecipeDefinition>>\n            PROP_GET_RECIPE = input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getRawValue().orElse(null));\n\n    public static final class Audio {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_VOLUME =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.volume\", AspectReadBuilders.VALIDATOR_DOUBLE_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_FREQUENCY =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.frequency\", AspectReadBuilders.VALIDATOR_DOUBLE_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROP_RANGE =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.range\", AspectReadBuilders.VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectProperties PROPERTIES_NOTE = new AspectProperties(Sets.<IAspectPropertyTypeInstance>newHashSet(\n                PROP_VOLUME\n        ));\n        public static final IAspectProperties PROPERTIES_SOUND = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROP_VOLUME,\n                PROP_FREQUENCY\n        ));\n        public static final IAspectProperties PROPERTIES_TEXT = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROP_VOLUME,\n                PROP_RANGE\n        ));\n        static {\n            PROPERTIES_NOTE.setValue(PROP_VOLUME, ValueTypeDouble.ValueDouble.of(3D));\n            PROPERTIES_SOUND.setValue(PROP_VOLUME, ValueTypeDouble.ValueDouble.of(3D));\n            PROPERTIES_SOUND.setValue(PROP_FREQUENCY, ValueTypeDouble.ValueDouble.of(1D));\n            PROPERTIES_TEXT.setValue(PROP_RANGE, ValueTypeInteger.ValueInteger.of(32));\n            PROPERTIES_TEXT.setValue(PROP_VOLUME, ValueTypeDouble.ValueDouble.of(3D));\n        }\n\n        public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, Pair<NoteBlockInstrument, Integer>>, Void> PROP_SET = input -> {\n            IAspectProperties properties = input.getMiddle();\n            BlockPos pos = input.getLeft().getTarget().getPos().getBlockPos();\n\n            NoteBlockInstrument instrument = input.getRight().getLeft();\n            int eventParam = input.getRight().getRight();\n            if(eventParam >= 0 && eventParam <= 24) {\n                Level world = input.getLeft().getTarget().getPos().getLevel(false);\n                if (world != null) { // If a block falls in a world when there's no one around, does it make any sound?\n                    NoteBlockEvent.Play e = new NoteBlockEvent.Play(world, pos, world.getBlockState(pos), eventParam, instrument);\n                    if (!net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(e).isCanceled()) {\n                        float f = (float) Math.pow(2.0D, (double) (eventParam - 12) / 12.0D);\n                        float volume = (float) properties.getValue(PROP_VOLUME).getRawValue();\n                        world.playSound(null, pos, e.getInstrument().getSoundEvent().value(), SoundSource.RECORDS, volume, f);\n                    }\n                }\n            }\n            return null;\n        };\n\n        public static IAspectValuePropagator<Triple<PartTarget, IAspectProperties, Integer>, Triple<PartTarget, IAspectProperties, Pair<NoteBlockInstrument, Integer>>>\n            propWithInstrument(final NoteBlockInstrument instrument) {\n            return input -> Triple.of(input.getLeft(), input.getMiddle(), Pair.of(instrument, input.getRight()));\n        }\n\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Triple<PartTarget, IAspectProperties, Integer>>\n                BUILDER_INTEGER = AspectWriteBuilders.BUILDER_INTEGER.appendKind(\"audio\").handle(PROP_GET_INTEGER);\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Triple<PartTarget, IAspectProperties, Integer>>\n                BUILDER_INTEGER_INSTRUMENT = BUILDER_INTEGER.appendKind(\"instrument\").withProperties(PROPERTIES_NOTE);\n        public static final AspectBuilder<ValueTypeString.ValueString, ValueTypeString, Triple<PartTarget, IAspectProperties, String>>\n                BUILDER_STRING = AspectWriteBuilders.BUILDER_STRING.appendKind(\"audio\").handle(PROP_GET_STRING);\n\n    }\n\n    public static final class Effect {\n\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_OFFSET_X =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.offset_x\");\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_OFFSET_Y =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.offset_y\");\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_OFFSET_Z =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.offset_z\");\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROP_PARTICLES =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.particles\", AspectReadBuilders.VALIDATOR_INTEGER_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_SPREAD_X =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.spread_x\", AspectReadBuilders.VALIDATOR_DOUBLE_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_SPREAD_Y =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.spread_y\", AspectReadBuilders.VALIDATOR_DOUBLE_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeDouble, ValueTypeDouble.ValueDouble> PROP_SPREAD_Z =\n                new AspectPropertyTypeInstance<>(ValueTypes.DOUBLE, \"aspect.aspecttypes.integrateddynamics.double.spread_z\", AspectReadBuilders.VALIDATOR_DOUBLE_POSITIVE);\n        public static final IAspectPropertyTypeInstance<ValueTypeBoolean, ValueTypeBoolean.ValueBoolean> PROP_FORCE =\n                new AspectPropertyTypeInstance<>(ValueTypes.BOOLEAN, \"aspect.aspecttypes.integrateddynamics.boolean.force_particle\");\n        public static final IAspectProperties PROPERTIES_PARTICLE = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROP_OFFSET_X,\n                PROP_OFFSET_Y,\n                PROP_OFFSET_Z,\n                PROP_PARTICLES,\n                PROP_SPREAD_X,\n                PROP_SPREAD_Y,\n                PROP_SPREAD_Z\n        ));\n\n        static {\n            PROPERTIES_PARTICLE.setValue(PROP_OFFSET_X, ValueTypeDouble.ValueDouble.of(0.5D));\n            PROPERTIES_PARTICLE.setValue(PROP_OFFSET_Z, ValueTypeDouble.ValueDouble.of(0.5D));\n            PROPERTIES_PARTICLE.setValue(PROP_OFFSET_Y, ValueTypeDouble.ValueDouble.of(0.5D));\n            PROPERTIES_PARTICLE.setValue(PROP_PARTICLES, ValueTypeInteger.ValueInteger.of(1));\n            PROPERTIES_PARTICLE.setValue(PROP_SPREAD_X, ValueTypeDouble.ValueDouble.of(0.0D));\n            PROPERTIES_PARTICLE.setValue(PROP_SPREAD_Y, ValueTypeDouble.ValueDouble.of(0.0D));\n            PROPERTIES_PARTICLE.setValue(PROP_SPREAD_Z, ValueTypeDouble.ValueDouble.of(0.0D));\n        }\n\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, Triple<PartTarget, IAspectProperties, Double>>\n                BUILDER_DOUBLE = AspectWriteBuilders.BUILDER_DOUBLE.appendKind(\"effect\").handle(PROP_GET_DOUBLE);\n        public static final AspectBuilder<ValueTypeDouble.ValueDouble, ValueTypeDouble, Triple<PartTarget, IAspectProperties, Double>>\n                BUILDER_DOUBLE_PARTICLE = BUILDER_DOUBLE.withProperties(PROPERTIES_PARTICLE);\n\n    }\n\n    public static final class Redstone {\n\n        private static final IWriteRedstoneComponent WRITE_REDSTONE_COMPONENT = new WriteRedstoneComponent();\n\n        public static final IAspectPropertyTypeInstance<ValueTypeBoolean, ValueTypeBoolean.ValueBoolean> PROP_STRONG_POWER =\n                new AspectPropertyTypeInstance<>(ValueTypes.BOOLEAN, \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\");\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROP_PULSE_EMIT_VALUE =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\",\n                        (v) -> v.getRawValue() >= 0 && v.getRawValue() <= 15);\n        public static final IAspectPropertyTypeInstance<ValueTypeInteger, ValueTypeInteger.ValueInteger> PROP_PULSE_LENGTH =\n                new AspectPropertyTypeInstance<>(ValueTypes.INTEGER, \"aspect.aspecttypes.integrateddynamics.integer.pulse_length\",\n                        (v) -> v.getRawValue() >= 1);\n        public static final IAspectProperties PROPERTIES_REDSTONE = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROP_STRONG_POWER\n        ));\n        public static final IAspectProperties PROPERTIES_REDSTONE_PULSE = new AspectProperties(ImmutableList.<IAspectPropertyTypeInstance>of(\n                PROP_STRONG_POWER,\n                PROP_PULSE_EMIT_VALUE,\n                PROP_PULSE_LENGTH\n        ));\n\n        static {\n            PROPERTIES_REDSTONE.setValue(PROP_STRONG_POWER, ValueTypeBoolean.ValueBoolean.of(false));\n\n            PROPERTIES_REDSTONE_PULSE.setValue(PROP_STRONG_POWER, ValueTypeBoolean.ValueBoolean.of(false));\n            PROPERTIES_REDSTONE_PULSE.setValue(PROP_PULSE_EMIT_VALUE, ValueTypeInteger.ValueInteger.of(15));\n            PROPERTIES_REDSTONE_PULSE.setValue(PROP_PULSE_LENGTH, ValueTypeInteger.ValueInteger.of(1));\n        }\n\n        public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, Integer>, Void> PROP_SET = input -> {\n            boolean strongPower = input.getMiddle().getValue(PROP_STRONG_POWER).getRawValue();\n            WRITE_REDSTONE_COMPONENT.setRedstoneLevel(input.getLeft(), input.getRight(), strongPower);\n            return null;\n        };\n        public static final IAspectValuePropagator<Triple<PartTarget, IAspectProperties, Integer>, Void> PROP_SET_PULSE = input -> {\n            PartTarget target = input.getLeft();\n            boolean strongPower = input.getMiddle().getValue(PROP_STRONG_POWER).getRawValue();\n            int pulseValue = input.getRight();\n            int emitLevel = input.getMiddle().getValue(PROP_PULSE_EMIT_VALUE).getRawValue();\n            int pulseLength = input.getMiddle().getValue(PROP_PULSE_LENGTH).getRawValue();\n            int lastPulseValue = WRITE_REDSTONE_COMPONENT.getLastPulseValue(target);\n            int scheduledPulseRemaining = WRITE_REDSTONE_COMPONENT.getScheduledPulseRemaining(target);\n            if (lastPulseValue != pulseValue) {\n                // New pulse triggered\n                WRITE_REDSTONE_COMPONENT.setLastPulseValue(target, pulseValue);\n                WRITE_REDSTONE_COMPONENT.setRedstoneLevel(target, emitLevel, strongPower);\n                WRITE_REDSTONE_COMPONENT.setScheduledPulseRemaining(target, pulseLength);\n            } else if (scheduledPulseRemaining == 0) {\n                // No pulse is active, set to 0\n                WRITE_REDSTONE_COMPONENT.setRedstoneLevel(target, 0, strongPower);\n            }\n            // If scheduledPulseRemaining > 0, the pulse is still active, don't change anything\n            return null;\n        };\n\n        public static final IAspectWriteDeactivator DEACTIVATOR = new IAspectWriteDeactivator() {\n            @Override\n            public <P extends IPartTypeWriter<P, S>, S extends IPartStateWriter<P>> void onDeactivate(P partType, PartTarget target, S state) {\n                WRITE_REDSTONE_COMPONENT.deactivate(target);\n            }\n        };\n\n        public static final AspectBuilder<ValueTypeBoolean.ValueBoolean, ValueTypeBoolean, Triple<PartTarget, IAspectProperties, Boolean>>\n                BUILDER_BOOLEAN = AspectWriteBuilders.BUILDER_BOOLEAN.appendKind(\"redstone\").handle(PROP_GET_BOOLEAN).appendDeactivator(DEACTIVATOR).withProperties(PROPERTIES_REDSTONE);\n        public static final AspectBuilder<ValueTypeInteger.ValueInteger, ValueTypeInteger, Triple<PartTarget, IAspectProperties, Integer>>\n                BUILDER_INTEGER = AspectWriteBuilders.BUILDER_INTEGER.appendKind(\"redstone\").handle(PROP_GET_INTEGER).appendDeactivator(DEACTIVATOR).withProperties(PROPERTIES_REDSTONE);\n\n    }\n\n    public static <V extends IValue, T extends IValueType<V>> AspectBuilder<V, T, Triple<PartTarget, IAspectProperties, V>> getValue(AspectBuilder<V, T, Triple<PartTarget, IAspectProperties, IVariable<V>>> builder) {\n        return builder.handle(input -> Triple.of(input.getLeft(), input.getMiddle(), input.getRight().getValue()));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/write/redstone/IWriteRedstoneComponent.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.write.redstone;\n\nimport net.minecraft.core.Direction;\nimport java.util.Optional;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\n\n/**\n * Interface for redstone writing the component.\n * @author rubensworks\n */\npublic interface IWriteRedstoneComponent {\n\n    public void setRedstoneLevel(PartTarget target, int level, boolean strongPower);\n\n    public void setLastPulseValue(PartTarget target, int value);\n\n    public int getLastPulseValue(PartTarget target);\n\n    public void setScheduledPulseRemaining(PartTarget target, int ticks);\n\n    public int getScheduledPulseRemaining(PartTarget target);\n\n    public void deactivate(PartTarget target);\n\n    public Optional<IDynamicRedstone> getDynamicRedstoneBlock(DimPos dimPos, Direction side);\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/part/aspect/write/redstone/WriteRedstoneComponent.java",
    "content": "package org.cyclops.integrateddynamics.part.aspect.write.redstone;\n\nimport net.minecraft.core.Direction;\nimport org.cyclops.cyclopscore.datastructure.DimPos;\nimport org.cyclops.cyclopscore.helper.IModHelpersNeoForge;\nimport org.cyclops.integrateddynamics.Capabilities;\nimport org.cyclops.integrateddynamics.api.block.IDynamicRedstone;\nimport org.cyclops.integrateddynamics.api.part.PartTarget;\nimport org.cyclops.integrateddynamics.capability.dynamicredstone.DynamicRedstoneHolderGlobal;\n\nimport java.util.Optional;\n\n/**\n * Default component for writing redstone levels.\n * @author rubensworks\n */\npublic class WriteRedstoneComponent implements IWriteRedstoneComponent {\n\n    public IDynamicRedstone getGlobalDynamicRedstone(PartTarget target) {\n        DimPos dimPos = target.getTarget().getPos();\n        Direction side = target.getTarget().getSide();\n        // We offset one block in the opposite direction of the target side, so that we output the redstone towards the target position\n        return DynamicRedstoneHolderGlobal.getInstance().getDynamicRedstone(dimPos.withPosition(dimPos.getBlockPos().relative(side)), side);\n    }\n\n    @Override\n    public void setRedstoneLevel(PartTarget target, int level, boolean strongPower) {\n        if (target.isDefaultTarget()) {\n            DimPos dimPos = target.getCenter().getPos();\n            getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                    .ifPresent(block -> block.setRedstoneLevel(level, strongPower));\n        } else {\n            getGlobalDynamicRedstone(target).setRedstoneLevel(level, strongPower);\n        }\n    }\n\n    @Override\n    public void setLastPulseValue(PartTarget target, int value) {\n        DimPos dimPos = target.getCenter().getPos();\n        getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                .ifPresent(block -> block.setLastPulseValue(value));\n    }\n\n    @Override\n    public int getLastPulseValue(PartTarget target) {\n        DimPos dimPos = target.getCenter().getPos();\n        return getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                .map(block -> block.getLastPulseValue())\n                .orElse(0);\n    }\n\n    @Override\n    public void setScheduledPulseRemaining(PartTarget target, int ticks) {\n        DimPos dimPos = target.getCenter().getPos();\n        getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                .ifPresent(block -> block.setScheduledPulseRemaining(ticks));\n    }\n\n    @Override\n    public int getScheduledPulseRemaining(PartTarget target) {\n        DimPos dimPos = target.getCenter().getPos();\n        return getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                .map(block -> block.getScheduledPulseRemaining())\n                .orElse(0);\n    }\n\n    @Override\n    public void deactivate(PartTarget target) {\n        if (target.isDefaultTarget()) {\n            DimPos dimPos = target.getCenter().getPos();\n            getDynamicRedstoneBlock(dimPos, target.getCenter().getSide())\n                    .ifPresent(block -> {\n                        block.setRedstoneLevel(-1, block.isDirect());\n                        block.setScheduledPulseRemaining(0);\n                    });\n        } else {\n            IDynamicRedstone block = getGlobalDynamicRedstone(target);\n            block.setRedstoneLevel(-1, block.isDirect());\n            block.setScheduledPulseRemaining(0);\n        }\n    }\n\n    @Override\n    public Optional<IDynamicRedstone> getDynamicRedstoneBlock(DimPos dimPos, Direction side) {\n        return IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(dimPos, side, Capabilities.DynamicRedstone.BLOCK);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/proxy/ClientProxy.java",
    "content": "package org.cyclops.integrateddynamics.proxy;\n\nimport com.mojang.blaze3d.platform.InputConstants;\nimport net.minecraft.client.KeyMapping;\nimport net.minecraft.client.renderer.texture.TextureAtlas;\nimport net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent;\nimport net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent;\nimport net.neoforged.neoforge.client.event.TextureAtlasStitchedEvent;\nimport net.neoforged.neoforge.client.settings.KeyConflictContext;\nimport net.neoforged.neoforge.client.settings.KeyModifier;\nimport net.neoforged.neoforge.common.NeoForge;\nimport org.cyclops.cyclopscore.client.key.IKeyRegistry;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.proxy.ClientProxyComponent;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.client.render.level.PartOffsetsOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDataClient;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnosticsPartOverlayRenderer;\nimport org.cyclops.integrateddynamics.core.network.diagnostics.http.DiagnosticsWebServer;\nimport org.lwjgl.glfw.GLFW;\n\n/**\n * Client Proxy\n * @author rubensworks\n */\npublic class ClientProxy extends ClientProxyComponent {\n\n    public static DiagnosticsWebServer DIAGNOSTICS_SERVER;\n\n    public static KeyMapping FOCUS_LP_SEARCH;\n    public static KeyMapping FOCUS_LP_RENAME;\n\n    public ClientProxy() {\n        super(new CommonProxy());\n        IntegratedDynamics._instance.getModEventBus().addListener(this::onPostTextureStitch);\n        NeoForge.EVENT_BUS.addListener(this::onPlayerLoggedOut);\n    }\n\n    @Override\n    public ModBaseNeoForge getMod() {\n        return IntegratedDynamics._instance;\n    }\n\n    @Override\n    public void registerEventHooks() {\n        super.registerEventHooks();\n        NeoForge.EVENT_BUS.register(NetworkDiagnosticsPartOverlayRenderer.getInstance());\n        NeoForge.EVENT_BUS.register(PartOffsetsOverlayRenderer.getInstance());\n    }\n\n    @Override\n    public void registerKeyBindings(IKeyRegistry keyRegistry, RegisterKeyMappingsEvent event) {\n        super.registerKeyBindings(keyRegistry, event);\n        event.register(FOCUS_LP_SEARCH = new KeyMapping(\n                \"key.\" + Reference.MOD_ID + \".logic_programmer_focus_search\",\n                KeyConflictContext.GUI, KeyModifier.ALT, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_F,\n                getMainKeyCategory()));\n        event.register(FOCUS_LP_RENAME = new KeyMapping(\n                \"key.\" + Reference.MOD_ID + \".logic_programmer_open_rename\",\n                KeyConflictContext.GUI, KeyModifier.ALT, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_R,\n                getMainKeyCategory()));\n    }\n\n    public void onPostTextureStitch(TextureAtlasStitchedEvent event) {\n        if (event.getAtlas().location().equals(TextureAtlas.LOCATION_BLOCKS)) {\n            event.getAtlas().getSprite(SlotVariable.VARIABLE_EMPTY);\n        }\n    }\n\n    public void onPlayerLoggedOut(ClientPlayerNetworkEvent.LoggingOut event) {\n        if (DIAGNOSTICS_SERVER != null) {\n            IntegratedDynamics.clog(\"Stopping diagnostics server...\");\n            NetworkDiagnosticsPartOverlayRenderer.getInstance().clearPositions();\n            NetworkDataClient.clearNetworkData();\n            PartOffsetsOverlayRenderer.getInstance().clear();\n            DIAGNOSTICS_SERVER.deinitialize();\n            DIAGNOSTICS_SERVER = null;\n            IntegratedDynamics.clog(\"Stopped diagnostics server\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/proxy/CommonProxy.java",
    "content": "package org.cyclops.integrateddynamics.proxy;\n\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.network.IPacketHandler;\nimport org.cyclops.cyclopscore.proxy.CommonProxyComponent;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.network.packet.*;\n\n/**\n * Common proxy\n * @author rubensworks\n */\npublic class CommonProxy extends CommonProxyComponent {\n\n    @Override\n    public ModBaseNeoForge getMod() {\n        return IntegratedDynamics._instance;\n    }\n\n    @Override\n    public void registerPackets(IPacketHandler packetHandler) {\n        super.registerPackets(packetHandler);\n\n        // Register packets.\n        packetHandler.register(LogicProgrammerActivateElementPacket.class, LogicProgrammerActivateElementPacket.ID, LogicProgrammerActivateElementPacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeStringValueChangedPacket.class, LogicProgrammerValueTypeStringValueChangedPacket.ID, LogicProgrammerValueTypeStringValueChangedPacket.CODEC);\n        packetHandler.register(ActionLabelPacket.class, ActionLabelPacket.TYPE, ActionLabelPacket.CODEC);\n        packetHandler.register(AllLabelsPacket.class, AllLabelsPacket.ID, AllLabelsPacket.CODEC);\n        packetHandler.register(ItemStackRenamePacket.class, ItemStackRenamePacket.ID, ItemStackRenamePacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeListValueChangedPacket.class, LogicProgrammerValueTypeListValueChangedPacket.ID, LogicProgrammerValueTypeListValueChangedPacket.CODEC);\n        packetHandler.register(LogicProgrammerLabelPacket.class, LogicProgrammerLabelPacket.ID, LogicProgrammerLabelPacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeOperatorValueChangedPacket.class, LogicProgrammerValueTypeOperatorValueChangedPacket.ID, LogicProgrammerValueTypeOperatorValueChangedPacket.CODEC);\n        packetHandler.register(NetworkDiagnosticsSubscribePacket.class, NetworkDiagnosticsSubscribePacket.ID, NetworkDiagnosticsSubscribePacket.CODEC);\n        packetHandler.register(NetworkDiagnosticsNetworkPacket.class, NetworkDiagnosticsNetworkPacket.ID, NetworkDiagnosticsNetworkPacket.CODEC);\n        packetHandler.register(NetworkDiagnosticsTriggerClient.class, NetworkDiagnosticsTriggerClient.ID, NetworkDiagnosticsTriggerClient.CODEC);\n        packetHandler.register(PlayerTeleportPacket.class, PlayerTeleportPacket.ID, PlayerTeleportPacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeSlottedValueChangedPacket.class, LogicProgrammerValueTypeSlottedValueChangedPacket.ID, LogicProgrammerValueTypeSlottedValueChangedPacket.CODEC);\n        packetHandler.register(LogicProgrammerSetElementInventory.class, LogicProgrammerSetElementInventory.ID, LogicProgrammerSetElementInventory.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeIngredientsValueChangedPacket.class, LogicProgrammerValueTypeIngredientsValueChangedPacket.ID, LogicProgrammerValueTypeIngredientsValueChangedPacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeRecipeValueChangedPacket.class, LogicProgrammerValueTypeRecipeValueChangedPacket.ID, LogicProgrammerValueTypeRecipeValueChangedPacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket.class, LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket.ID, LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket.CODEC);\n        packetHandler.register(SpeakTextPacket.class, SpeakTextPacket.ID, SpeakTextPacket.CODEC);\n        packetHandler.register(LogicProgrammerValueTypeBooleanValueChangedPacket.class, LogicProgrammerValueTypeBooleanValueChangedPacket.ID, LogicProgrammerValueTypeBooleanValueChangedPacket.CODEC);\n        packetHandler.register(PartOffsetsSubscribePacket.class, PartOffsetsSubscribePacket.ID, PartOffsetsSubscribePacket.CODEC);\n        packetHandler.register(PartOffsetsDataPacket.class, PartOffsetsDataPacket.ID, PartOffsetsDataPacket.CODEC);\n\n        IntegratedDynamics.clog(\"Registered packet handler.\");\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/ItemFacadeRecipe.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.BlockItem;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.*;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * Recipe for combining facades with blocks.\n * @author rubensworks\n *\n */\npublic class ItemFacadeRecipe extends CustomRecipe {\n\n    private NonNullList<Ingredient> ingredients;\n\n    public ItemFacadeRecipe() {\n        super();\n    }\n\n    @Override\n    public boolean matches(CraftingInput grid, Level world) {\n        return !assemble(grid).isEmpty();\n    }\n\n    public ItemStack getResultItem() {\n        return new ItemStack(RegistryEntries.ITEM_FACADE);\n    }\n\n    @Override\n    public NonNullList<ItemStack> getRemainingItems(CraftingInput inventory) {\n        NonNullList<ItemStack> aitemstack = NonNullList.withSize(inventory.size(), ItemStack.EMPTY);\n\n        for (int i = 0; i < aitemstack.size(); ++i) {\n            ItemStack itemstack = inventory.getItem(i);\n            net.minecraft.world.item.ItemStackTemplate remainder = itemstack.getItem().getCraftingRemainder(itemstack);\n            aitemstack.set(i, remainder != null ? remainder.create() : ItemStack.EMPTY);\n        }\n\n        return aitemstack;\n    }\n\n    @Override\n    public ItemStack assemble(CraftingInput grid) {\n        ItemStack output = getResultItem().copy();\n\n        int facades = 0;\n        ItemStack block = ItemStack.EMPTY;\n\n        for(int j = 0; j < grid.size(); j++) {\n            ItemStack element = grid.getItem(j);\n            if(!element.isEmpty()) {\n                if(element.getItem() == output.getItem()) {\n                    facades++;\n                } else if(block.isEmpty() && element.getItem() instanceof BlockItem\n                        && !((BlockItem) element.getItem()).getBlock().defaultBlockState().useShapeForLightOcclusion()) {\n                    block = element;\n                } else {\n                    return ItemStack.EMPTY;\n                }\n            }\n        }\n\n        if(facades != 1 || block.isEmpty()) {\n            return ItemStack.EMPTY;\n        }\n\n        RegistryEntries.ITEM_FACADE.get().writeFacadeBlock(output, IModHelpers.get().getBlockHelpers().getBlockStateFromItemStack(block));\n        return output;\n    }\n\n    @Override\n    public boolean isSpecial() {\n        return true;\n    }\n\n    @Override\n    public RecipeSerializer<? extends CustomRecipe> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_FACADE.get();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/ItemFacadeRecipeConfig.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ItemFacadeRecipe}.\n * @author rubensworks\n */\npublic class ItemFacadeRecipeConfig extends RecipeConfigCommon<ItemFacadeRecipe, IntegratedDynamics> {\n\n    public static final RecipeSerializer<ItemFacadeRecipe> SERIALIZER;\n    static {\n        ItemFacadeRecipe recipe = new ItemFacadeRecipe();\n        SERIALIZER = new RecipeSerializer<>(\n                MapCodec.unit(recipe),\n                StreamCodec.unit(recipe)\n        );\n    }\n\n    public ItemFacadeRecipeConfig() {\n        super(IntegratedDynamics._instance,\n                \"crafting_special_facade\",\n                eConfig -> SERIALIZER);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/ItemVariableCopyRecipe.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingInput;\nimport net.minecraft.world.item.crafting.CustomRecipe;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.minecraft.world.level.Level;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacade;\nimport org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;\nimport org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;\nimport org.cyclops.integrateddynamics.item.ItemVariable;\n\n/**\n * Crafting recipe to copy variable data.\n * @author rubensworks\n */\npublic class ItemVariableCopyRecipe extends CustomRecipe {\n\n    private ValueDeseralizationContext lastValueDeseralizationContext;\n\n    public ItemVariableCopyRecipe() {\n        super();\n    }\n\n    @Override\n    public boolean matches(CraftingInput inv, Level worldIn) {\n        lastValueDeseralizationContext = ValueDeseralizationContext.of(worldIn);\n        return !assemble(inv).isEmpty();\n    }\n\n    @Override\n    public ItemStack assemble(CraftingInput inv) {\n        ItemStack withData = ItemStack.EMPTY;\n        ItemStack withoutData = ItemStack.EMPTY;\n        IVariableFacade facade;\n        int count = 0;\n        for(int j = 0; j < inv.size(); j++) {\n            ItemStack element = inv.getItem(j);\n            if(!element.isEmpty() && element.getItem() instanceof ItemVariable) {\n                count++;\n                facade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(lastValueDeseralizationContext, element);\n                if(!facade.isValid() && withoutData.isEmpty()) {\n                    withoutData = element;\n                }\n                if(facade.isValid() && withData.isEmpty() && element.getCount() == 1) {\n                    withData = element.copy();\n                }\n            }\n        }\n        if(count == 2 && !withoutData.isEmpty() && !withData.isEmpty()) {\n            return withData;\n        }\n        return ItemStack.EMPTY;\n    }\n\n    public ItemStack getResultItem() {\n        return new ItemStack(RegistryEntries.ITEM_VARIABLE, 1);\n    }\n\n    @Override\n    public NonNullList<ItemStack> getRemainingItems(CraftingInput inv) {\n        NonNullList<ItemStack> ret = NonNullList.withSize(inv.size(), ItemStack.EMPTY);\n        for(int j = 0; j < inv.size(); j++) {\n            ItemStack element = inv.getItem(j);\n            if(!element.isEmpty() && element.getItem() instanceof ItemVariable) {\n                IVariableFacade facade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(lastValueDeseralizationContext, element);\n                if(facade.isValid()) {\n                    // Create a copy with a new id.\n                    ItemStack copy = IntegratedDynamics._instance.getRegistryManager()\n                            .getRegistry(IVariableFacadeHandlerRegistry.class).copy(!IModHelpers.get().getMinecraftHelpers().isClientSideThread(), element);\n\n                    // If the input had a label, also copy the label\n                    String label = LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().getLabel(facade.getId());\n                    if(label != null) {\n                        IVariableFacade facadeCopy = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(lastValueDeseralizationContext, copy);\n                        if (facadeCopy != null) {\n                            LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().put(facadeCopy.getId(), label);\n                        }\n                    }\n\n                    ret.set(j, copy);\n                }\n            }\n        }\n        return ret;\n    }\n\n    @Override\n    public boolean isSpecial() {\n        return true;\n    }\n\n    @Override\n    public RecipeSerializer<? extends CustomRecipe> getSerializer() {\n        return RegistryEntries.RECIPESERIALIZER_VARIABLE_COPY.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/ItemVariableCopyRecipeConfig.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport com.mojang.serialization.MapCodec;\nimport net.minecraft.network.codec.StreamCodec;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link ItemVariableCopyRecipe}.\n * @author rubensworks\n */\npublic class ItemVariableCopyRecipeConfig extends RecipeConfigCommon<ItemVariableCopyRecipe, IntegratedDynamics> {\n\n    public static final RecipeSerializer<ItemVariableCopyRecipe> SERIALIZER;\n    static {\n        ItemVariableCopyRecipe recipe = new ItemVariableCopyRecipe();\n        SERIALIZER = new RecipeSerializer<>(\n                MapCodec.unit(recipe),\n                StreamCodec.unit(recipe)\n        );\n    }\n\n    public ItemVariableCopyRecipeConfig() {\n        super(IntegratedDynamics._instance,\n                \"crafting_special_variable_copy\",\n                eConfig -> SERIALIZER);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/RecipeSerializerCraftingSpecialShapedOmniDirectional3Config.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport net.minecraft.world.item.ItemStackTemplate;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.cyclopscore.recipe.type.RecipeCraftingShapedCustomOutput;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypeConnectorOmniDirectional;\n\n/**\n * @author rubensworks\n */\npublic class RecipeSerializerCraftingSpecialShapedOmniDirectional3Config extends RecipeConfigCommon<RecipeCraftingShapedCustomOutput, IntegratedDynamics> {\n\n    public RecipeSerializerCraftingSpecialShapedOmniDirectional3Config() {\n        super(IntegratedDynamics._instance,\n                \"crafting_special_shaped_omni_directional_3\",\n                eConfig -> new RecipeCraftingShapedCustomOutput.Serializer(\n                        () -> new ItemStackTemplate(PartTypes.CONNECTOR_OMNI.getItem(), 3),\n                        PartTypeConnectorOmniDirectional::transformCraftingOutput).getRecipeSerializer()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/RecipeSerializerCraftingSpecialShapedOmniDirectionalConfig.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport net.minecraft.world.item.ItemStackTemplate;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.cyclopscore.recipe.type.RecipeCraftingShapedCustomOutput;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypeConnectorOmniDirectional;\n\n/**\n * @author rubensworks\n */\npublic class RecipeSerializerCraftingSpecialShapedOmniDirectionalConfig extends RecipeConfigCommon<RecipeCraftingShapedCustomOutput, IntegratedDynamics> {\n\n    public RecipeSerializerCraftingSpecialShapedOmniDirectionalConfig() {\n        super(IntegratedDynamics._instance,\n                \"crafting_special_shaped_omni_directional\",\n                eConfig -> new RecipeCraftingShapedCustomOutput.Serializer(\n                        () -> new ItemStackTemplate(PartTypes.CONNECTOR_OMNI.getItem(), 2),\n                        PartTypeConnectorOmniDirectional::transformCraftingOutput).getRecipeSerializer()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/recipe/RecipeSerializerCraftingSpecialShapelessOmniDirectionalConfig.java",
    "content": "package org.cyclops.integrateddynamics.recipe;\n\nimport net.minecraft.world.item.ItemStackTemplate;\nimport org.cyclops.cyclopscore.config.extendedconfig.RecipeConfigCommon;\nimport org.cyclops.cyclopscore.init.ModBaseNeoForge;\nimport org.cyclops.cyclopscore.recipe.type.RecipeCraftingShapelessCustomOutput;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\nimport org.cyclops.integrateddynamics.core.part.PartTypes;\nimport org.cyclops.integrateddynamics.part.PartTypeConnectorOmniDirectional;\n\n/**\n * @author rubensworks\n */\npublic class RecipeSerializerCraftingSpecialShapelessOmniDirectionalConfig extends RecipeConfigCommon<RecipeCraftingShapelessCustomOutput, ModBaseNeoForge<?>> {\n\n    public RecipeSerializerCraftingSpecialShapelessOmniDirectionalConfig() {\n        super(IntegratedDynamics._instance,\n                \"crafting_special_shapeless_omni_directional\",\n                eConfig -> new RecipeCraftingShapelessCustomOutput.Serializer(\n                        () -> new ItemStackTemplate(PartTypes.CONNECTOR_OMNI.getItem(), 2),\n                        PartTypeConnectorOmniDirectional::transformCraftingOutput).getRecipeSerializer()\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/sound/SoundEventEffectPageFlipMultipleConfig.java",
    "content": "package org.cyclops.integrateddynamics.sound;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.sounds.SoundEvent;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class SoundEventEffectPageFlipMultipleConfig extends org.cyclops.cyclopscore.config.extendedconfig.SoundEventConfigCommon<IModBase> {\n    public SoundEventEffectPageFlipMultipleConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"effect_page_flipmultiple\",\n                (eConfig) -> SoundEvent.createVariableRangeEvent(Identifier.fromNamespaceAndPath(eConfig.getMod().getModId(), eConfig.getNamedId()))\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/sound/SoundEventEffectPageFlipSingleConfig.java",
    "content": "package org.cyclops.integrateddynamics.sound;\n\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.sounds.SoundEvent;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * @author rubensworks\n */\npublic class SoundEventEffectPageFlipSingleConfig extends org.cyclops.cyclopscore.config.extendedconfig.SoundEventConfigCommon<IModBase> {\n    public SoundEventEffectPageFlipSingleConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"effect_page_flipsingle\",\n                (eConfig) -> SoundEvent.createVariableRangeEvent(Identifier.fromNamespaceAndPath(eConfig.getMod().getModId(), eConfig.getNamedId()))\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/world/gen/foliageplacer/FoliagePlacerMenril.java",
    "content": "package org.cyclops.integrateddynamics.world.gen.foliageplacer;\n\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.util.valueproviders.IntProvider;\nimport net.minecraft.world.level.WorldGenLevel;\nimport net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;\nimport net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;\nimport net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacerType;\nimport org.cyclops.integrateddynamics.RegistryEntries;\n\n/**\n * @author rubensworks\n */\npublic class FoliagePlacerMenril extends FoliagePlacer {\n    public static final MapCodec<FoliagePlacerMenril> CODEC = RecordCodecBuilder.mapCodec((builder) -> foliagePlacerParts(builder)\n            .apply(builder, FoliagePlacerMenril::new));\n\n    public FoliagePlacerMenril(IntProvider radius, IntProvider offset) {\n        super(radius, offset);\n    }\n\n    protected FoliagePlacerType<?> type() {\n        return RegistryEntries.FOLIAGE_PLACER_MENRIL.get();\n    }\n\n    @Override\n    protected void createFoliage(WorldGenLevel world, FoliagePlacer.FoliageSetter callback, RandomSource rand, TreeConfiguration config,\n                                 int mimimumHeight, FoliagePlacer.FoliageAttachment foliage, int foliageHeight, int spread, int offset) {\n        BlockPos blockpos = foliage.pos();\n        for(int l = offset; l >= -foliageHeight; --l) {\n            int radius = (l == offset || l == -foliageHeight) ? 1 : 2;\n            this.placeLeavesRow(world, callback, rand, config, blockpos, radius, l, foliage.doubleTrunk());\n        }\n    }\n\n    @Override\n    public int foliageHeight(RandomSource rand, int treeHeight, TreeConfiguration config) {\n        return 5;\n    }\n\n    @Override\n    protected boolean shouldSkipLocation(RandomSource rand, int p_230373_2_, int p_230373_3_, int p_230373_4_, int p_230373_5_, boolean p_230373_6_) {\n        return p_230373_2_ == p_230373_5_ && p_230373_4_ == p_230373_5_ && p_230373_5_ > 0; // ???\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/world/gen/foliageplacer/FoliagePlacerMenrilConfig.java",
    "content": "package org.cyclops.integrateddynamics.world.gen.foliageplacer;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.FoliagePlacerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link FoliagePlacerMenril}.\n * @author rubensworks\n *\n */\npublic class FoliagePlacerMenrilConfig extends FoliagePlacerConfigCommon<FoliagePlacerMenril, IntegratedDynamics> {\n\n    public FoliagePlacerMenrilConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril\",\n                eConfig -> FoliagePlacerMenril.CODEC\n        );\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/world/gen/trunkplacer/TrunkPlacerMenril.java",
    "content": "package org.cyclops.integrateddynamics.world.gen.trunkplacer;\n\nimport com.google.common.collect.ImmutableList;\nimport com.mojang.serialization.Codec;\nimport com.mojang.serialization.MapCodec;\nimport com.mojang.serialization.codecs.RecordCodecBuilder;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.level.WorldGenLevel;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.levelgen.feature.TreeFeature;\nimport net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;\nimport net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;\nimport net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer;\nimport net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;\nimport org.cyclops.integrateddynamics.RegistryEntries;\nimport org.cyclops.integrateddynamics.block.BlockMenrilLogFilled;\n\nimport java.util.List;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\n/**\n * @author rubensworks\n */\npublic class TrunkPlacerMenril extends TrunkPlacer {\n    public static final MapCodec<TrunkPlacerMenril> CODEC = RecordCodecBuilder.mapCodec((builder) -> trunkPlacerParts(builder)\n            .and(Codec.intRange(0, 32).fieldOf(\"trunk_height_wider\").forGetter((placer) -> placer.heightWider))\n            .apply(builder, TrunkPlacerMenril::new));\n\n    protected final int heightWider;\n\n    public TrunkPlacerMenril(int baseHeight, int heightRandA, int heightRandB, int heightWider) {\n        super(baseHeight, heightRandA, heightRandB);\n        this.heightWider = heightWider;\n    }\n\n    @Override\n    protected TrunkPlacerType<?> type() {\n        return RegistryEntries.TRUNK_PLACER_MENRIL.get();\n    }\n\n    @Override\n    public List<FoliagePlacer.FoliageAttachment> placeTrunk(WorldGenLevel world, BiConsumer<BlockPos, BlockState> callback, RandomSource rand, int height,\n                                                            BlockPos pos, TreeConfiguration config) {\n        // Only generate if stump is fully on ground (other checks are done in TreeFeature.place)\n        BlockPos basePos = pos;\n        if (!TreeFeature.isAirOrLeaves(world, basePos.north())\n                && !TreeFeature.isAirOrLeaves(world, basePos.east())\n                && !TreeFeature.isAirOrLeaves(world, basePos.south())\n                && !TreeFeature.isAirOrLeaves(world, basePos.west())) {\n            // all adjacent positions are blocked, skip\n            return ImmutableList.of();\n        }\n\n        // Ensure dirt is below tree\n        BlockPos posStump = pos.below();\n        setDirtAt(world, callback, rand, posStump, config);\n\n        // Create stump\n        if (placeLog(world, callback, rand, pos.north(), config, Function.identity())) {\n            setDirtAt(world, callback, rand, posStump.north(), config);\n        }\n        if (placeLog(world, callback, rand, pos.east(), config, Function.identity())) {\n            setDirtAt(world, callback, rand, posStump.east(), config);\n        }\n        if (placeLog(world, callback, rand, pos.south(), config, Function.identity())) {\n            setDirtAt(world, callback, rand, posStump.south(), config);\n        }\n        if (placeLog(world, callback, rand, pos.west(), config, Function.identity())) {\n            setDirtAt(world, callback, rand, posStump.west(), config);\n        }\n\n        // Create base trunk\n        for(int i = 0; i < height; ++i) {\n            placeLog(world, callback, rand, pos.above(i), config, Function.identity());\n        }\n\n        // Create wider trunk\n        for(int i = height; i < height + heightWider; ++i) {\n            BlockPos posIt = pos.above(i);\n            placeLog(world, callback, rand, posIt, config, Function.identity());\n            placeLog(world, callback, rand, posIt.north(), config, Function.identity());\n            placeLog(world, callback, rand, posIt.east(), config, Function.identity());\n            placeLog(world, callback, rand, posIt.south(), config, Function.identity());\n            placeLog(world, callback, rand, posIt.west(), config, Function.identity());\n        }\n\n        return ImmutableList.of(new FoliagePlacer.FoliageAttachment(pos.above(height + heightWider), 0 /*radius*/, false));\n    }\n\n    protected boolean placeLog(WorldGenLevel world, BiConsumer<BlockPos, BlockState> callback, RandomSource rand, BlockPos pos, TreeConfiguration config, Function<BlockState, BlockState> transformer) {\n        if (TreeFeature.validTreePos(world, pos)) {\n            BlockState logs = transformer.apply(config.trunkProvider.getState(world, rand, pos));\n            logs = logs.getBlock() instanceof BlockMenrilLogFilled\n                    ? logs.setValue(BlockMenrilLogFilled.SIDE, Direction.Plane.HORIZONTAL.getRandomDirection(rand))\n                    : logs;\n            callback.accept(pos, logs);\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    protected void setDirtAt(WorldGenLevel world, BiConsumer<BlockPos, BlockState> callback, RandomSource rand, BlockPos pos, TreeConfiguration config) {\n        placeBelowTrunkBlock(world, callback, rand, pos, config);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cyclops/integrateddynamics/world/gen/trunkplacer/TrunkPlacerMenrilConfig.java",
    "content": "package org.cyclops.integrateddynamics.world.gen.trunkplacer;\n\nimport org.cyclops.cyclopscore.config.extendedconfig.TrunkPlacerConfigCommon;\nimport org.cyclops.integrateddynamics.IntegratedDynamics;\n\n/**\n * Config for {@link TrunkPlacerMenril}.\n * @author rubensworks\n *\n */\npublic class TrunkPlacerMenrilConfig extends TrunkPlacerConfigCommon<TrunkPlacerMenril, IntegratedDynamics> {\n\n    public TrunkPlacerMenrilConfig() {\n        super(\n                IntegratedDynamics._instance,\n                \"menril\",\n                eConfig -> TrunkPlacerMenril.CODEC\n        );\n    }\n\n}\n"
  },
  {
    "path": "src/main/resources/LICENSE-netty.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        https://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\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"
  },
  {
    "path": "src/main/resources/LICENSE-re2j.txt",
    "content": "This is a work derived from Russ Cox's RE2 in Go, whose license\nhttp://golang.org/LICENSE is as follows:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\n     notice, this list of conditions and the following disclaimer.\n\n   * Redistributions in binary form must reproduce the above copyright\n     notice, this list of conditions and the following disclaimer in\n     the documentation and/or other materials provided with the\n     distribution.\n\n   * Neither the name of Google Inc. nor the names of its contributors\n     may be used to endorse or promote products derived from this\n     software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "src/main/resources/META-INF/accesstransformer.cfg",
    "content": "# Level\npublic net.minecraft.world.level.Level skyDarken\n\n# GUIs\npublic net.minecraft.client.gui.components.AbstractWidget focused\npublic net.minecraft.world.inventory.AbstractContainerMenu lastSlots\npublic net.minecraft.world.inventory.AbstractContainerMenu remoteSlots\n\n# Shapes\npublic net.minecraft.world.phys.shapes.VoxelShape getCoords(Lnet/minecraft/core/Direction$Axis;)Lit/unimi/dsi/fastutil/doubles/DoubleList;\npublic net.minecraft.world.phys.shapes.SliceShape getCoords(Lnet/minecraft/core/Direction$Axis;)Lit/unimi/dsi/fastutil/doubles/DoubleList;\npublic net.minecraft.world.phys.shapes.ArrayVoxelShape getCoords(Lnet/minecraft/core/Direction$Axis;)Lit/unimi/dsi/fastutil/doubles/DoubleList;\npublic net.minecraft.world.phys.shapes.CubeVoxelShape getCoords(Lnet/minecraft/core/Direction$Axis;)Lit/unimi/dsi/fastutil/doubles/DoubleList;\npublic net.minecraft.world.phys.shapes.VoxelShape collideX(Lnet/minecraft/core/AxisCycle;Lnet/minecraft/world/phys/AABB;D)D\npublic net.minecraft.world.phys.shapes.Shapes BLOCK\npublic net.minecraft.world.phys.shapes.VoxelShape shape\npublic net.minecraft.world.phys.shapes.VoxelShape <init>(Lnet/minecraft/world/phys/shapes/DiscreteVoxelShape;)V\n\n# We must make the following methods in all sub-classes of LivingEntity as well, otherwise we run into compilation issues with Forge.\n## getHurtSound\npublic net.minecraft.world.entity.LivingEntity getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.player.Player getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.decoration.ArmorStand getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.ambient.Bat getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Monster getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Blaze getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.feline.Cat getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.spider.Spider getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.chicken.Chicken getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.Cod getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.cow.AbstractCow getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Creeper getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Donkey getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.dolphin.Dolphin getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.happyghast.HappyGhast getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.Zombie getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.Drowned getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Guardian getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.ElderGuardian getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.boss.enderdragon.EnderDragon getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.EnderMan getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Endermite getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Evoker getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fox.Fox getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Ghast getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Horse getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.Husk getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Illusioner getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Llama getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Slime getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.MagmaCube getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Mule getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.feline.Ocelot getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.panda.Panda getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.parrot.Parrot getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.pig.Pig getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.Pufferfish getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.polarbear.PolarBear getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.rabbit.Rabbit getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.Salmon getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.sheep.Sheep getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.AbstractGolem getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Shulker getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Silverfish getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Skeleton getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.SkeletonHorse getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.SnowGolem getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.squid.Squid getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Stray getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.TropicalFish getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.turtle.Turtle getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Vex getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.npc.villager.Villager getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.IronGolem getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Vindicator getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Pillager getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Witch getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.boss.wither.WitherBoss getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.WitherSkeleton getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.wolf.Wolf getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.ZombieHorse getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Phantom getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Ravager getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.bee.Bee getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.hoglin.Hoglin getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.piglin.Piglin getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.piglin.PiglinBrute getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Strider getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Zoglin getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.ZombifiedPiglin getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.axolotl.Axolotl getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.squid.GlowSquid getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.goat.Goat getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.warden.Warden getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.allay.Allay getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.frog.Frog getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.frog.Tadpole getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.camel.Camel getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.sniffer.Sniffer getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.breeze.Breeze getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.armadillo.Armadillo getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Bogged getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.creaking.Creaking getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.CopperGolem getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.camel.CamelHusk getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.nautilus.Nautilus getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Parched getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.nautilus.ZombieNautilus getHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)Lnet/minecraft/sounds/SoundEvent;\n\n## getDeathSound\npublic net.minecraft.world.entity.LivingEntity getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.player.Player getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.decoration.ArmorStand getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.ambient.Bat getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Monster getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Blaze getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.feline.Cat getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.spider.Spider getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.chicken.Chicken getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.Cod getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.cow.AbstractCow getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Creeper getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Donkey getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.dolphin.Dolphin getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.happyghast.HappyGhast getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.Zombie getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.Drowned getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Guardian getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.ElderGuardian getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.EnderMan getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Endermite getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Evoker getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fox.Fox getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Ghast getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Horse getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.Husk getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Illusioner getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Llama getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Slime getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.MagmaCube getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.Mule getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.feline.Ocelot getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.panda.Panda getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.parrot.Parrot getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.pig.Pig getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.Pufferfish getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.polarbear.PolarBear getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.rabbit.Rabbit getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.Salmon getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.sheep.Sheep getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.AbstractGolem getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Shulker getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Silverfish getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Skeleton getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.SkeletonHorse getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.SnowGolem getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.squid.Squid getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Stray getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.fish.TropicalFish getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.turtle.Turtle getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Vex getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.npc.villager.Villager getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.IronGolem getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Vindicator getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.illager.Pillager getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Witch getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.boss.wither.WitherBoss getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.WitherSkeleton getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.wolf.Wolf getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.equine.ZombieHorse getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Phantom getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Ravager getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.bee.Bee getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.hoglin.Hoglin getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.piglin.Piglin getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.piglin.PiglinBrute getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Strider getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.Zoglin getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.zombie.ZombifiedPiglin getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.axolotl.Axolotl getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.squid.GlowSquid getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.goat.Goat getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.warden.Warden getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.allay.Allay getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.frog.Frog getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.frog.Tadpole getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.camel.Camel getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.sniffer.Sniffer getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.breeze.Breeze getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.armadillo.Armadillo getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Bogged getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.creaking.Creaking getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.golem.CopperGolem getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.camel.CamelHusk getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.nautilus.Nautilus getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.monster.skeleton.Parched getDeathSound()Lnet/minecraft/sounds/SoundEvent;\npublic net.minecraft.world.entity.animal.nautilus.ZombieNautilus getDeathSound()Lnet/minecraft/sounds/SoundEvent;\n\n# BuiltInRegistries\npublic net.minecraft.core.registries.BuiltInRegistries WRITABLE_REGISTRY\n\n# RenderSectionRegion\npublic net.minecraft.client.renderer.chunk.RenderSectionRegion level"
  },
  {
    "path": "src/main/resources/META-INF/neoforge.mods.toml",
    "content": "modLoader=\"javafml\"\nloaderVersion=\"${neoforge_loader_version_range}\"\nissueTrackerURL=\"${issue_tracker_url}\"\ndisplayURL=\"${display_url}\"\nlicense=\"${license}\"\nlogoFile=\"logo.png\"\nauthors=\"${mod_author}\"\n[[mods]]\nmodId=\"${mod_id}\"\nversion=\"${mod_version}\"\ndisplayName=\"${mod_name}\"\nupdateJSONURL=\"${neoforge_update_json_url}\"\ndescription=\"${description}\"\n[[accessTransformers]]\n    file=\"META-INF/accesstransformer.cfg\"\n[[mixins]]\n    config=\"mixins.${mod_id}.json\"\n[[dependencies.${mod_id}]]\n    modId=\"cyclopscore\"\n    type=\"required\"\n    versionRange=\"[${cyclopscore_version_semver},)\"\n    ordering=\"AFTER\"\n    side=\"BOTH\"\n[[dependencies.${mod_id}]]\n    modId=\"commoncapabilities\"\n    type=\"required\"\n    versionRange=\"[${commoncapabilities_version_semver},)\"\n    ordering=\"NONE\"\n    side=\"BOTH\"\n[[dependencies.${mod_id}]]\n    modId=\"neoforge\"\n    type=\"required\"\n    versionRange=\"[${neoforge_version},)\"\n    ordering=\"NONE\"\n    side=\"BOTH\"\n[[dependencies.${mod_id}]]\n    modId=\"minecraft\"\n    type=\"required\"\n    versionRange=\"[${minecraft_version},]\"\n    ordering=\"NONE\"\n    side=\"BOTH\"\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/block_liquid_chorus.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/liquid_chorus\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/block_menril_resin.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/menril_resin\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/cable.json",
    "content": "{\n  \"_comment\": \"Dummy blockstate\",\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/menril_torch\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/coal_generator.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,lit=false\": { \"model\": \"integrateddynamics:block/coal_generator\" },\n    \"facing=south,lit=false\": { \"model\": \"integrateddynamics:block/coal_generator\", \"y\": 180 },\n    \"facing=west,lit=false\":  { \"model\": \"integrateddynamics:block/coal_generator\", \"y\": 270 },\n    \"facing=east,lit=false\":  { \"model\": \"integrateddynamics:block/coal_generator\", \"y\": 90 },\n    \"facing=north,lit=true\": { \"model\": \"integrateddynamics:block/coal_generator_on\" },\n    \"facing=south,lit=true\": { \"model\": \"integrateddynamics:block/coal_generator_on\", \"y\": 180 },\n    \"facing=west,lit=true\":  { \"model\": \"integrateddynamics:block/coal_generator_on\", \"y\": 270 },\n    \"facing=east,lit=true\":  { \"model\": \"integrateddynamics:block/coal_generator_on\", \"y\": 90 }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_chorus_block.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_chorus_block_slab.json",
    "content": "{\n  \"variants\": {\n    \"type=bottom\": {\n      \"model\": \"integrateddynamics:block/crystalized_chorus_block_slab\"\n    },\n    \"type=double\": {\n      \"model\": \"integrateddynamics:block/crystalized_chorus_block\"\n    },\n    \"type=top\": {\n      \"model\": \"integrateddynamics:block/crystalized_chorus_block_slab_top\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_chorus_block_stairs.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\" },\n    \"facing=west,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\" },\n    \"facing=west,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\" },\n    \"facing=north,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\" },\n    \"facing=west,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\" },\n    \"facing=north,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_chorus_brick.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_chorus_brick_slab.json",
    "content": "{\n  \"variants\": {\n    \"type=bottom\": {\n      \"model\": \"integrateddynamics:block/crystalized_chorus_brick_slab\"\n    },\n    \"type=double\": {\n      \"model\": \"integrateddynamics:block/crystalized_chorus_brick\"\n    },\n    \"type=top\": {\n      \"model\": \"integrateddynamics:block/crystalized_chorus_brick_slab_top\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_chorus_brick_stairs.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\" },\n    \"facing=west,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\" },\n    \"facing=west,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\" },\n    \"facing=north,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\" },\n    \"facing=west,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\" },\n    \"facing=north,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_menril_block.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/crystalized_menril_block\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_menril_block_slab.json",
    "content": "{\n  \"variants\": {\n    \"type=bottom\": {\n      \"model\": \"integrateddynamics:block/crystalized_menril_block_slab\"\n    },\n    \"type=double\": {\n      \"model\": \"integrateddynamics:block/crystalized_menril_block\"\n    },\n    \"type=top\": {\n      \"model\": \"integrateddynamics:block/crystalized_menril_block_slab_top\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_menril_block_stairs.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\" },\n    \"facing=west,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\" },\n    \"facing=west,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\" },\n    \"facing=north,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\" },\n    \"facing=west,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\" },\n    \"facing=north,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_menril_brick.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_menril_brick_slab.json",
    "content": "{\n  \"variants\": {\n    \"type=bottom\": {\n      \"model\": \"integrateddynamics:block/crystalized_menril_brick_slab\"\n    },\n    \"type=double\": {\n      \"model\": \"integrateddynamics:block/crystalized_menril_brick\"\n    },\n    \"type=top\": {\n      \"model\": \"integrateddynamics:block/crystalized_menril_brick_slab_top\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/crystalized_menril_brick_stairs.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\" },\n    \"facing=west,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\" },\n    \"facing=west,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\" },\n    \"facing=north,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\" },\n    \"facing=west,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\" },\n    \"facing=north,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/delay.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/delay\" },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/delay\", \"y\": 180 },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/delay\", \"y\": 270 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/delay\", \"y\": 90 }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/drying_basin.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/drying_basin\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/energy_battery.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/energy_battery\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/energy_battery_creative.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/energy_battery_creative\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/invisible_light.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/invisible_light\" }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/logic_programmer.json",
    "content": "{\n  \"variants\": {\n    \"facing=south\": { \"model\": \"integrateddynamics:block/logic_programmer\" },\n    \"facing=west\" : { \"model\": \"integrateddynamics:block/logic_programmer\", \"y\": 90 },\n    \"facing=north\": { \"model\": \"integrateddynamics:block/logic_programmer\", \"y\": 180 },\n    \"facing=east\" : { \"model\": \"integrateddynamics:block/logic_programmer\", \"y\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/materializer.json",
    "content": "{\n  \"variants\": {\n    \"facing=south\": { \"model\": \"integrateddynamics:block/materializer\" },\n    \"facing=west\" : { \"model\": \"integrateddynamics:block/materializer\", \"y\": 90 },\n    \"facing=north\": { \"model\": \"integrateddynamics:block/materializer\", \"y\": 180 },\n    \"facing=east\" : { \"model\": \"integrateddynamics:block/materializer\", \"y\": 270 }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/mechanical_drying_basin.json",
    "content": "{\n  \"variants\": {\n    \"lit=false\": { \"model\": \"integrateddynamics:block/mechanical_drying_basin\" },\n    \"lit=true\": { \"model\": \"integrateddynamics:block/mechanical_drying_basin_on\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/mechanical_squeezer.json",
    "content": "{\n  \"variants\": {\n    \"lit=false\": { \"model\": \"integrateddynamics:block/mechanical_squeezer\" },\n    \"lit=true\": { \"model\": \"integrateddynamics:block/mechanical_squeezer_on\" }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_door.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,half=lower,hinge=left,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_bottom\" },\n    \"facing=south,half=lower,hinge=left,open=false\": { \"model\": \"integrateddynamics:block/menril_door_bottom\", \"y\": 90 },\n    \"facing=west,half=lower,hinge=left,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_bottom\", \"y\": 180 },\n    \"facing=north,half=lower,hinge=left,open=false\": { \"model\": \"integrateddynamics:block/menril_door_bottom\", \"y\": 270 },\n    \"facing=east,half=lower,hinge=right,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\" },\n    \"facing=south,half=lower,hinge=right,open=false\": { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\", \"y\": 90 },\n    \"facing=west,half=lower,hinge=right,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\", \"y\": 180 },\n    \"facing=north,half=lower,hinge=right,open=false\": { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\", \"y\": 270 },\n    \"facing=east,half=lower,hinge=left,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\", \"y\": 90 },\n    \"facing=south,half=lower,hinge=left,open=true\": { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\", \"y\": 180 },\n    \"facing=west,half=lower,hinge=left,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\", \"y\": 270 },\n    \"facing=north,half=lower,hinge=left,open=true\": { \"model\": \"integrateddynamics:block/menril_door_bottom_rh\" },\n    \"facing=east,half=lower,hinge=right,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_bottom\", \"y\": 270 },\n    \"facing=south,half=lower,hinge=right,open=true\": { \"model\": \"integrateddynamics:block/menril_door_bottom\" },\n    \"facing=west,half=lower,hinge=right,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_bottom\", \"y\": 90 },\n    \"facing=north,half=lower,hinge=right,open=true\": { \"model\": \"integrateddynamics:block/menril_door_bottom\", \"y\": 180 },\n    \"facing=east,half=upper,hinge=left,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_top\" },\n    \"facing=south,half=upper,hinge=left,open=false\": { \"model\": \"integrateddynamics:block/menril_door_top\", \"y\": 90 },\n    \"facing=west,half=upper,hinge=left,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_top\", \"y\": 180 },\n    \"facing=north,half=upper,hinge=left,open=false\": { \"model\": \"integrateddynamics:block/menril_door_top\", \"y\": 270 },\n    \"facing=east,half=upper,hinge=right,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_top_rh\" },\n    \"facing=south,half=upper,hinge=right,open=false\": { \"model\": \"integrateddynamics:block/menril_door_top_rh\", \"y\": 90 },\n    \"facing=west,half=upper,hinge=right,open=false\":  { \"model\": \"integrateddynamics:block/menril_door_top_rh\", \"y\": 180 },\n    \"facing=north,half=upper,hinge=right,open=false\": { \"model\": \"integrateddynamics:block/menril_door_top_rh\", \"y\": 270 },\n    \"facing=east,half=upper,hinge=left,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_top_rh\", \"y\": 90 },\n    \"facing=south,half=upper,hinge=left,open=true\": { \"model\": \"integrateddynamics:block/menril_door_top_rh\", \"y\": 180 },\n    \"facing=west,half=upper,hinge=left,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_top_rh\", \"y\": 270 },\n    \"facing=north,half=upper,hinge=left,open=true\": { \"model\": \"integrateddynamics:block/menril_door_top_rh\" },\n    \"facing=east,half=upper,hinge=right,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_top\", \"y\": 270 },\n    \"facing=south,half=upper,hinge=right,open=true\": { \"model\": \"integrateddynamics:block/menril_door_top\" },\n    \"facing=west,half=upper,hinge=right,open=true\":  { \"model\": \"integrateddynamics:block/menril_door_top\", \"y\": 90 },\n    \"facing=north,half=upper,hinge=right,open=true\": { \"model\": \"integrateddynamics:block/menril_door_top\", \"y\": 180 }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_fence.json",
    "content": "{\n  \"multipart\": [\n    {\n      \"apply\": {\n        \"model\": \"integrateddynamics:block/menril_fence_post\"\n      }\n    },\n    {\n      \"when\": {\n        \"north\": \"true\"\n      },\n      \"apply\": {\n        \"model\": \"integrateddynamics:block/menril_fence_side\",\n        \"uvlock\": true\n      }\n    },\n    {\n      \"when\": {\n        \"east\": \"true\"\n      },\n      \"apply\": {\n        \"model\": \"integrateddynamics:block/menril_fence_side\",\n        \"y\": 90,\n        \"uvlock\": true\n      }\n    },\n    {\n      \"when\": {\n        \"south\": \"true\"\n      },\n      \"apply\": {\n        \"model\": \"integrateddynamics:block/menril_fence_side\",\n        \"y\": 180,\n        \"uvlock\": true\n      }\n    },\n    {\n      \"when\": {\n        \"west\": \"true\"\n      },\n      \"apply\": {\n        \"model\": \"integrateddynamics:block/menril_fence_side\",\n        \"y\": 270,\n        \"uvlock\": true\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_fence_gate.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,in_wall=false,open=false\": {\n      \"uvlock\": true,\n      \"y\": 270,\n      \"model\": \"integrateddynamics:block/menril_fence_gate\"\n    },\n    \"facing=east,in_wall=false,open=true\": {\n      \"uvlock\": true,\n      \"y\": 270,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_open\"\n    },\n    \"facing=east,in_wall=true,open=false\": {\n      \"uvlock\": true,\n      \"y\": 270,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall\"\n    },\n    \"facing=east,in_wall=true,open=true\": {\n      \"uvlock\": true,\n      \"y\": 270,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall_open\"\n    },\n    \"facing=north,in_wall=false,open=false\": {\n      \"uvlock\": true,\n      \"y\": 180,\n      \"model\": \"integrateddynamics:block/menril_fence_gate\"\n    },\n    \"facing=north,in_wall=false,open=true\": {\n      \"uvlock\": true,\n      \"y\": 180,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_open\"\n    },\n    \"facing=north,in_wall=true,open=false\": {\n      \"uvlock\": true,\n      \"y\": 180,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall\"\n    },\n    \"facing=north,in_wall=true,open=true\": {\n      \"uvlock\": true,\n      \"y\": 180,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall_open\"\n    },\n    \"facing=south,in_wall=false,open=false\": {\n      \"uvlock\": true,\n      \"model\": \"integrateddynamics:block/menril_fence_gate\"\n    },\n    \"facing=south,in_wall=false,open=true\": {\n      \"uvlock\": true,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_open\"\n    },\n    \"facing=south,in_wall=true,open=false\": {\n      \"uvlock\": true,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall\"\n    },\n    \"facing=south,in_wall=true,open=true\": {\n      \"uvlock\": true,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall_open\"\n    },\n    \"facing=west,in_wall=false,open=false\": {\n      \"uvlock\": true,\n      \"y\": 90,\n      \"model\": \"integrateddynamics:block/menril_fence_gate\"\n    },\n    \"facing=west,in_wall=false,open=true\": {\n      \"uvlock\": true,\n      \"y\": 90,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_open\"\n    },\n    \"facing=west,in_wall=true,open=false\": {\n      \"uvlock\": true,\n      \"y\": 90,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall\"\n    },\n    \"facing=west,in_wall=true,open=true\": {\n      \"uvlock\": true,\n      \"y\": 90,\n      \"model\": \"integrateddynamics:block/menril_fence_gate_wall_open\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_leaves.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/menril_leaves\" }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_log.json",
    "content": "{\n    \"variants\": {\n        \"axis=y\": { \"model\": \"integrateddynamics:block/menril_log\" },\n        \"axis=z\": { \"model\": \"integrateddynamics:block/menril_log\", \"x\": 90 },\n        \"axis=x\": { \"model\": \"integrateddynamics:block/menril_log\", \"x\": 90, \"y\": 90 }\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_log_filled.json",
    "content": "{\n    \"variants\": {\n        \"axis=x,facing=north\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"y\": 90 },\n        \"axis=x,facing=east\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"y\": 90, \"x\": 90 },\n        \"axis=x,facing=south\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"y\": 90, \"x\": 180 },\n        \"axis=x,facing=west\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"y\": 90, \"x\": 270 },\n        \"axis=y,facing=north\": { \"model\": \"integrateddynamics:block/menril_log_filled\" },\n        \"axis=y,facing=east\": { \"model\": \"integrateddynamics:block/menril_log_filled\", \"y\": 90 },\n        \"axis=y,facing=south\": { \"model\": \"integrateddynamics:block/menril_log_filled\", \"y\": 180 },\n        \"axis=y,facing=west\": { \"model\": \"integrateddynamics:block/menril_log_filled\", \"y\": 270 },\n        \"axis=z,facing=north\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\" },\n        \"axis=z,facing=east\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"x\": 90 },\n        \"axis=z,facing=south\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"x\": 180 },\n        \"axis=z,facing=west\": { \"model\": \"integrateddynamics:block/menril_log_filled_side\", \"x\": 270 }\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_log_stripped.json",
    "content": "{\n    \"variants\": {\n        \"axis=y\": { \"model\": \"integrateddynamics:block/menril_log_stripped\" },\n        \"axis=z\": { \"model\": \"integrateddynamics:block/menril_log_stripped\", \"x\": 90 },\n        \"axis=x\": { \"model\": \"integrateddynamics:block/menril_log_stripped\", \"x\": 90, \"y\": 90 }\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_planks.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/menril_planks\" }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_planks_stairs.json",
    "content": "{\n  \"variants\": {\n    \"facing=east,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs\" },\n    \"facing=west,half=bottom,shape=straight\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=straight\": { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\" },\n    \"facing=west,half=bottom,shape=outer_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=outer_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=outer_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\" },\n    \"facing=north,half=bottom,shape=outer_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\" },\n    \"facing=west,half=bottom,shape=inner_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=bottom,shape=inner_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"y\": 270, \"uvlock\": true },\n    \"facing=west,half=bottom,shape=inner_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"y\": 90, \"uvlock\": true },\n    \"facing=south,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\" },\n    \"facing=north,half=bottom,shape=inner_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"y\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=straight\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=straight\": { \"model\": \"integrateddynamics:block/menril_planks_stairs\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=outer_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=outer_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_outer\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_right\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_right\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=east,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"uvlock\": true },\n    \"facing=west,half=top,shape=inner_left\":  { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"y\": 180, \"uvlock\": true },\n    \"facing=south,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"y\": 90, \"uvlock\": true },\n    \"facing=north,half=top,shape=inner_left\": { \"model\": \"integrateddynamics:block/menril_planks_stairs_inner\", \"x\": 180, \"y\": 270, \"uvlock\": true }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_sapling.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\": \"integrateddynamics:block/menril_sapling\" }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_slab.json",
    "content": "{\n  \"variants\": {\n    \"type=bottom\": {\n      \"model\": \"integrateddynamics:block/menril_slab\"\n    },\n    \"type=double\": {\n      \"model\": \"integrateddynamics:block/menril_planks\"\n    },\n    \"type=top\": {\n      \"model\": \"integrateddynamics:block/menril_slab_top\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_torch.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\" : \"integrateddynamics:block/menril_torch\" }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_torch_stone.json",
    "content": "{\n  \"variants\": {\n    \"\": { \"model\" : \"integrateddynamics:block/menril_torch_stone\" }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_torch_stone_wall.json",
    "content": "{\n  \"variants\": {\n    \"facing=east\": { \"model\": \"integrateddynamics:block/menril_torch_stone_wall\" },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/menril_torch_stone_wall\", \"y\": 90 },\n    \"facing=west\": { \"model\": \"integrateddynamics:block/menril_torch_stone_wall\", \"y\": 180 },\n    \"facing=north\": { \"model\": \"integrateddynamics:block/menril_torch_stone_wall\", \"y\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_torch_wall.json",
    "content": "{\n  \"variants\": {\n    \"facing=east\": { \"model\": \"integrateddynamics:block/menril_torch_wall\" },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/menril_torch_wall\", \"y\": 90 },\n    \"facing=west\": { \"model\": \"integrateddynamics:block/menril_torch_wall\", \"y\": 180 },\n    \"facing=north\": { \"model\": \"integrateddynamics:block/menril_torch_wall\", \"y\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_wood.json",
    "content": "{\n    \"variants\": {\n        \"axis=y\": { \"model\": \"integrateddynamics:block/menril_wood\" },\n        \"axis=z\": { \"model\": \"integrateddynamics:block/menril_wood\", \"x\": 90 },\n        \"axis=x\": { \"model\": \"integrateddynamics:block/menril_wood\", \"x\": 90, \"y\": 90 }\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/menril_wood_stripped.json",
    "content": "{\n    \"variants\": {\n        \"axis=y\": { \"model\": \"integrateddynamics:block/menril_wood_stripped\" },\n        \"axis=z\": { \"model\": \"integrateddynamics:block/menril_wood_stripped\", \"x\": 90 },\n        \"axis=x\": { \"model\": \"integrateddynamics:block/menril_wood_stripped\", \"x\": 90, \"y\": 90 }\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_audio_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_audio_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_audio_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_audio_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_audio_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_audio_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_audio_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_audio_writer.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_audio_writer_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_audio_writer_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_audio_writer_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_audio_writer_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_audio_writer_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_audio_writer_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_audio_writer_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_audio_writer_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_audio_writer_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_audio_writer_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_audio_writer_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_audio_writer_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_audio_writer_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_audio_writer_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_audio_writer_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_audio_writer_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_audio_writer_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_audio_writer_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_block_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_block_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_block_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_block_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_block_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_block_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_block_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_connector_mono_directional.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_connector_mono_directional_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_connector_mono_directional_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_connector_mono_directional_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_connector_mono_directional_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_connector_mono_directional_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_connector_mono_directional_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_connector_mono_directional_active\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_connector_omni_directional.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_connector_omni_directional_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_connector_omni_directional_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_connector_omni_directional_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_connector_omni_directional_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_connector_omni_directional_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_connector_omni_directional_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_connector_omni_directional_active\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_display_panel.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_display_panel_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_display_panel_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_display_panel_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_display_panel_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_display_panel_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_display_panel_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_display_panel_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_display_panel_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_display_panel_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_display_panel_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_display_panel_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_display_panel_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_display_panel_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_display_panel_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_display_panel_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_display_panel_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_display_panel_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_display_panel_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_dynamic_light_panel.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_dynamic_light_panel_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_effect_writer.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_effect_writer_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_effect_writer_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_effect_writer_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_effect_writer_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_effect_writer_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_effect_writer_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_effect_writer_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_effect_writer_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_effect_writer_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_effect_writer_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_effect_writer_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_effect_writer_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_effect_writer_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_effect_writer_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_effect_writer_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_effect_writer_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_effect_writer_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_effect_writer_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_entity_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_entity_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_entity_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_entity_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_entity_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_entity_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_entity_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_entity_writer.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_entity_writer_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_entity_writer_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_entity_writer_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_entity_writer_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_entity_writer_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_entity_writer_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_entity_writer_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_entity_writer_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_entity_writer_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_entity_writer_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_entity_writer_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_entity_writer_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_entity_writer_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_entity_writer_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_entity_writer_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_entity_writer_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_entity_writer_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_entity_writer_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_extradimensional_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_extradimensional_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_extradimensional_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_extradimensional_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_extradimensional_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_extradimensional_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_extradimensional_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_fluid_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_fluid_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_fluid_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_fluid_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_fluid_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_fluid_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_fluid_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_inventory_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_inventory_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_inventory_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_inventory_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_inventory_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_inventory_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_inventory_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_inventory_writer.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_inventory_writer_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_inventory_writer_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_inventory_writer_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_inventory_writer_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_inventory_writer_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_inventory_writer_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_inventory_writer_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_inventory_writer_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_inventory_writer_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_inventory_writer_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_inventory_writer_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_inventory_writer_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_machine_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_machine_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_machine_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_machine_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_machine_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_machine_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_machine_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_machine_writer.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_machine_writer_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_machine_writer_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_machine_writer_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_machine_writer_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_machine_writer_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_machine_writer_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_machine_writer_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_machine_writer_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_machine_writer_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_machine_writer_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_machine_writer_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_machine_writer_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_machine_writer_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_machine_writer_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_machine_writer_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_machine_writer_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_machine_writer_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_machine_writer_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_network_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_network_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_network_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_network_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_network_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_network_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_network_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_redstone_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_redstone_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_redstone_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_redstone_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_redstone_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_redstone_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_redstone_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_redstone_writer.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,status=active\":   { \"model\": \"integrateddynamics:block/part_redstone_writer_active\", \"y\": 180 },\n    \"facing=east,status=active\":    { \"model\": \"integrateddynamics:block/part_redstone_writer_active\", \"y\": 270 },\n    \"facing=south,status=active\":   { \"model\": \"integrateddynamics:block/part_redstone_writer_active\"           },\n    \"facing=west,status=active\":    { \"model\": \"integrateddynamics:block/part_redstone_writer_active\", \"y\": 90  },\n    \"facing=up,status=active\":      { \"model\": \"integrateddynamics:block/part_redstone_writer_active\", \"x\": 90  },\n    \"facing=down,status=active\":    { \"model\": \"integrateddynamics:block/part_redstone_writer_active\", \"x\": 270 },\n\n    \"facing=north,status=inactive\": { \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\", \"y\": 180 },\n    \"facing=east,status=inactive\":  { \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\", \"y\": 270 },\n    \"facing=south,status=inactive\": { \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\"           },\n    \"facing=west,status=inactive\":  { \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\", \"y\": 90  },\n    \"facing=up,status=inactive\":    { \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\", \"x\": 90  },\n    \"facing=down,status=inactive\":  { \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\", \"x\": 270 },\n\n    \"facing=north,status=error\":    { \"model\": \"integrateddynamics:block/part_redstone_writer_error\", \"y\": 180 },\n    \"facing=east,status=error\":     { \"model\": \"integrateddynamics:block/part_redstone_writer_error\", \"y\": 270 },\n    \"facing=south,status=error\":    { \"model\": \"integrateddynamics:block/part_redstone_writer_error\"           },\n    \"facing=west,status=error\":     { \"model\": \"integrateddynamics:block/part_redstone_writer_error\", \"y\": 90  },\n    \"facing=up,status=error\":       { \"model\": \"integrateddynamics:block/part_redstone_writer_error\", \"x\": 90  },\n    \"facing=down,status=error\":     { \"model\": \"integrateddynamics:block/part_redstone_writer_error\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_static_light_panel.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_static_light_panel\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_static_light_panel\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_static_light_panel\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_static_light_panel\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_static_light_panel\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_static_light_panel\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/part_world_reader.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": { \"model\": \"integrateddynamics:block/part_world_reader\", \"y\": 180 },\n    \"facing=east\":  { \"model\": \"integrateddynamics:block/part_world_reader\", \"y\": 270 },\n    \"facing=south\": { \"model\": \"integrateddynamics:block/part_world_reader\"           },\n    \"facing=west\":  { \"model\": \"integrateddynamics:block/part_world_reader\", \"y\": 90  },\n    \"facing=up\":    { \"model\": \"integrateddynamics:block/part_world_reader\", \"x\": 90  },\n    \"facing=down\":  { \"model\": \"integrateddynamics:block/part_world_reader\", \"x\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/proxy.json",
    "content": "{\n  \"variants\": {\n    \"facing=south\": { \"model\": \"integrateddynamics:block/proxy\" },\n    \"facing=west\" : { \"model\": \"integrateddynamics:block/proxy\", \"y\": 90 },\n    \"facing=north\": { \"model\": \"integrateddynamics:block/proxy\", \"y\": 180 },\n    \"facing=east\" : { \"model\": \"integrateddynamics:block/proxy\", \"y\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/squeezer.json",
    "content": "{\n  \"multipart\": [\n    {\n      \"when\": { \"axis\": \"x\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer\" }\n    },\n    {\n      \"when\": { \"axis\": \"z\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer\", \"y\": 90 }\n    },\n    {\n      \"when\": { \"height\": \"1\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_1\" }\n    },\n    {\n      \"when\": { \"height\": \"2\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_2\" }\n    },\n    {\n      \"when\": { \"height\": \"3\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_3\" }\n    },\n    {\n      \"when\": { \"height\": \"4\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_4\" }\n    },\n    {\n      \"when\": { \"height\": \"5\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_5\" }\n    },\n    {\n      \"when\": { \"height\": \"6\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_6\" }\n    },\n    {\n      \"when\": { \"height\": \"7\" },\n      \"apply\": { \"model\": \"integrateddynamics:block/squeezer_top_7\" }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/blockstates/variablestore.json",
    "content": "{\n  \"variants\": {\n    \"facing=south\": { \"model\": \"integrateddynamics:block/variablestore\" },\n    \"facing=west\" : { \"model\": \"integrateddynamics:block/variablestore\", \"y\": 90 },\n    \"facing=north\": { \"model\": \"integrateddynamics:block/variablestore\", \"y\": 180 },\n    \"facing=east\" : { \"model\": \"integrateddynamics:block/variablestore\", \"y\": 270 }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/block_liquid_chorus.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/liquid_chorus\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/block_menril_resin.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_resin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/bucket_liquid_chorus.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/bucket_liquid_chorus\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/bucket_menril_resin.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/bucket_menril_resin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/cable.json",
    "content": "{\n  \"model\": {\n    \"type\": \"integrateddynamics:cable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/coal_generator.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/coal_generator\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_block.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_block_slab.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_chorus_block_slab\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_block_stairs.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_chorus_block_stairs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_brick.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_brick_slab.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_chorus_brick_slab\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_brick_stairs.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_chorus_chunk.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/crystalized_chorus_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_block.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_block_slab.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_menril_block_slab\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_block_stairs.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_menril_block_stairs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_brick.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_brick_slab.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_menril_brick_slab\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_brick_stairs.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/crystalized_menril_brick_stairs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/crystalized_menril_chunk.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/crystalized_menril_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/delay.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/delay\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/drying_basin.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/drying_basin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/energy_battery.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:composite\",\n    \"models\": [\n      {\n        \"type\": \"minecraft:model\",\n        \"model\": \"integrateddynamics:block/energy_battery\"\n      },\n      {\n        \"type\": \"minecraft:special\",\n        \"base\": \"integrateddynamics:block/energy_battery\",\n        \"model\": {\n          \"type\": \"integrateddynamics:energy_battery\"\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/energy_battery_creative.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:composite\",\n    \"models\": [\n      {\n        \"type\": \"minecraft:model\",\n        \"model\": \"integrateddynamics:block/energy_battery\"\n      },\n      {\n        \"type\": \"minecraft:special\",\n        \"base\": \"integrateddynamics:block/energy_battery\",\n        \"model\": {\n          \"type\": \"integrateddynamics:energy_battery\"\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/enhancement_offset.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/enhancement_offset\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/facade.json",
    "content": "{\n  \"model\": {\n    \"type\": \"integrateddynamics:facade\",\n    \"model_empty\": \"integrateddynamics:item/facade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/invisible_light.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/invisible_light\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/labeller.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/labeller\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/logic_director.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/logic_director\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/logic_programmer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/logic_programmer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/materializer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/materializer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/mechanical_drying_basin.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/mechanical_drying_basin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/mechanical_squeezer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/mechanical_squeezer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_berries.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/menril_berries\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_door.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/menril_door\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_fence.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/menril_fence\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_fence_gate.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_fence_gate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_leaves.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_leaves\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_log.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_log\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_log_filled.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_log_filled\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_log_stripped.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_log_stripped\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_planks.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_planks_stairs.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_planks_stairs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_sapling.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/menril_sapling\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_slab.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_slab\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_torch.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/menril_torch\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_torch_stone.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/menril_torch_stone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_wood.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_wood\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/menril_wood_stripped.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/menril_wood_stripped\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/on_the_dynamics_of_integration.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/on_the_dynamics_of_integration\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_audio_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_audio_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_audio_writer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_audio_writer_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_block_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_block_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_connector_mono_directional.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_connector_mono_directional\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_connector_omni_directional.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_connector_omni_directional\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_display_panel.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_display_panel_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_dynamic_light_panel.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_effect_writer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_effect_writer_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_entity_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_entity_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_entity_writer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_entity_writer_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_extradimensional_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_extradimensional_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_fluid_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_fluid_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_inventory_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_inventory_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_inventory_writer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_inventory_writer_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_machine_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_machine_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_machine_writer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_machine_writer_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_network_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_network_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_redstone_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_redstone_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_redstone_writer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_redstone_writer_inactive\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_static_light_panel.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_static_light_panel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/part_world_reader.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/part_world_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/portable_logic_programmer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/portable_logic_programmer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/proto_chorus.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/proto_chorus\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/proxy.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/proxy\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/squeezer.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/squeezer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/variable.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:composite\",\n    \"models\": [\n      {\n        \"type\": \"minecraft:model\",\n        \"model\": \"integrateddynamics:item/variable\"\n      },\n      {\n        \"type\": \"integrateddynamics:variable_overlays\",\n        \"model\": \"integrateddynamics:item/variable_overlays\"\n      },\n      {\n        \"type\": \"minecraft:special\",\n        \"base\": \"integrateddynamics:item/variable\",\n        \"model\": {\n          \"type\": \"integrateddynamics:variable_overlay_special\"\n        }\n      }\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/variable_transformer_input.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/variable_transformer_input\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/variable_transformer_output.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/variable_transformer_output\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/variablestore.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:block/variablestore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/items/wrench.json",
    "content": "{\n  \"model\": {\n    \"type\": \"minecraft:model\",\n    \"model\": \"integrateddynamics:item/wrench\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/af_za.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/ar_sa.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/ca_es.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/cs_cz.json",
    "content": "{\n    \"_comment\": \"------ On the Dynamics of Integration contents ------\",\n    \"itemGroup.integrateddynamics\": \"Integrated Dynamics\",\n    \"general.integrateddynamics.description\": \"Převezmi plné a automatické ovládání tvých zařázení.\",\n    \"general.integrateddynamics.info.none\": \"Nic\",\n    \"general.integrateddynamics.energy\": \"Energie\",\n    \"general.integrateddynamics.energy_unit\": \"RF\",\n    \"general.integrateddynamics.error.no_network\": \"Nebyla nalezena žádná platná síť\",\n    \"general.integrateddynamics.true\": \"Ano\",\n    \"general.integrateddynamics.false\": \"Ne\",\n    \"gui.integrateddynamics.part_settings\": \"Nastavení částí\",\n    \"gui.integrateddynamics.aspect_settings\": \"Nastavení aspektů\",\n    \"gui.integrateddynamics.part_offsets\": \"Odsazení částí\",\n    \"gui.integrateddynamics.keybinding\": \"Klávesové zkratky\",\n    \"gui.integrateddynamics.button.save\": \"Uložit\",\n    \"gui.integrateddynamics.button.rename\": \"Přejmenovat\",\n    \"gui.integrateddynamics.button.copy\": \"Kopírovat\",\n    \"gui.integrateddynamics.button.copy.info\": \"Zkopírovat do schránky\",\n    \"gui.integrateddynamics.button.edit\": \"Upravit\",\n    \"gui.integrateddynamics.button.add\": \"Přidat\",\n    \"gui.integrateddynamics.button.remove\": \"Odstranit\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"Ticky/Operace\",\n    \"gui.integrateddynamics.partsettings.priority\": \"Priorita\",\n    \"gui.integrateddynamics.partsettings.channel\": \"Energetický kanál\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"Spotřeba energie ze sítě je vypnutá na tomto serveru.\",\n    \"gui.integrateddynamics.partsettings.side\": \"Směr pohybu\",\n    \"gui.integrateddynamics.partoffset.offsets\": \"Odsazení vzhledem k cílové pozici\",\n    \"gui.integrateddynamics.partoffset.offsets.max\": \"Maximální odsazení: %s\",\n    \"gui.integrateddynamics.partoffset.offsets.max.howtoincrease\": \"Zvyš limit s obohacením části: Odsazení\",\n    \"gui.integrateddynamics.partoffset.slot.message.noint\": \"Neplatná souřadnice čísla\",\n    \"gui.integrateddynamics.partoffset.slot.message.outofrange\": \"Odsazení přesahuje povolený rozsah\",\n    \"gui.integrateddynamics.partoffset.x\": \"Odchylka X\",\n    \"gui.integrateddynamics.partoffset.y\": \"Odchylka Y\",\n    \"gui.integrateddynamics.partoffset.z\": \"Odchylka Z\",\n    \"gui.integrateddynamics.delay.capacity\": \"Kapacita\",\n    \"gui.integrateddynamics.part.properties\": \"Vlastnosti:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"Filtrovat\",\n    \"gui.integrateddynamics.rewards\": \"Odměny\",\n    \"gui.integrateddynamics.rewards.collect\": \"Sebrat\",\n    \"gui.integrateddynamics.rewards.collected\": \"Sebráno\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"Nelze sebrat odměny, je tvůj inventář plný?\",\n    \"gui.integrateddynamics.input\": \"Vstup: %s\",\n    \"gui.integrateddynamics.output\": \"Výstup: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"Automaticky vysunout kapalinu\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Automaticky vysunout kapalinu: %s\",\n    \"gui.integrateddynamics.input_short\": \"Vstup\",\n    \"gui.integrateddynamics.output_short\": \"Výstup\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"Přísné NBT\",\n    \"gui.integrateddynamics.recipe.reusable\": \"Vicenásobně použitelné\",\n    \"gui.integrateddynamics.operator.globalname\": \"Globální jméno: %s\",\n    \"gui.integrateddynamics.operator.localname\": \"Jméno metody: %s\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"Části\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"Pozorovatelé úložiště\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Síť\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Kabely\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"Část\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Čas ticků (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"Dimenze\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"Pozice\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"Strana\",\n    \"gui.integrateddynamics.diagnostics.table.actions\": \"Akce\",\n    \"key.categories.integrateddynamics\": \"Integrated Dynamics\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"Otočit stránku\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"Otočit stránky\",\n    \"block.integrateddynamics.cable\": \"Logický kabel\",\n    \"block.integrateddynamics.variablestore\": \"Úložiště proměn\",\n    \"block.integrateddynamics.energy_battery\": \"Energetická baterie\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"Automatické zásobování: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"Tvořivá energetická baterie\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"Automatické zásobování: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"Logický programátor\",\n    \"block.integrateddynamics.coal_generator\": \"Generátor\",\n    \"block.integrateddynamics.coal_generator.info\": \"Vyrábí energii z palivových předmětů\",\n    \"block.integrateddynamics.proxy\": \"Proxy\",\n    \"block.integrateddynamics.materializer\": \"Materializér\",\n    \"block.integrateddynamics.materializer.info\": \"Zhmotnit hodnotu proměny v nové proměně\",\n    \"block.integrateddynamics.menril_log\": \"Menrilovníkový kmen\",\n    \"block.integrateddynamics.menril_log_filled\": \"Obohacený menrilovníkový kmen\",\n    \"block.integrateddynamics.menril_log_stripped\": \"Oloupaný menrilovníkový kmen\",\n    \"block.integrateddynamics.menril_leaves\": \"Menrilovníkové listy\",\n    \"block.integrateddynamics.menril_sapling\": \"Sazenice menrilovníku\",\n    \"block.integrateddynamics.menril_planks\": \"Menrilovníková prkna\",\n    \"block.integrateddynamics.menril_slab\": \"Menrilovníkový půlblok\",\n    \"block.integrateddynamics.menril_fence\": \"Menrilovníkový plot\",\n    \"block.integrateddynamics.menril_fence_gate\": \"Menrilovníková branka\",\n    \"block.integrateddynamics.menril_wood\": \"Menrilovníkové dřevo\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"Oloupané menrilovníkové dřevo\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Blok krystalizovaného menrilu\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"Krystalizovaná menrilová cihla\",\n    \"block.integrateddynamics.block_menril_resin\": \"Menrilovníková pryskyřice\",\n    \"block.integrateddynamics.drying_basin\": \"Sušící mísa\",\n    \"block.integrateddynamics.drying_basin.info\": \"Pro sušení předmětů a kapalin\",\n    \"block.integrateddynamics.squeezer\": \"Lisovač\",\n    \"block.integrateddynamics.squeezer.info\": \"Lisuje, zarovnává a drtí když na něj někdo skočí. Ruditový puls pro resetování.\",\n    \"block.integrateddynamics.menril_torch\": \"Menrilová louč\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Menrilová kamenná louč\",\n    \"block.integrateddynamics.invisible_light\": \"Neviditelné světlo\",\n    \"block.integrateddynamics.menril_door\": \"Menrilovníkové dveře\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Menrilovníkové schody\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Krystalizované menrilové schody\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"Schody z krystalizovaných menrilových cihel\",\n    \"block.integrateddynamics.delay\": \"Zpožďovač\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"Tekutý chorus\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"Blok krystalovaného chorusu\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"Krystalová chorusová cihla\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"Mechanický lisovač\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"Mechanická sušící mísa\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"Pro sušení předmětů a kapalin pomocí energie.\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"Krystalizovaný menrilový půlblok\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"Půlblok krystalizovaných menrilových cihel\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"Krystalový chorusový půlblok\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"Půlblok krystalizovaných chorusových cihel\",\n    \"item.integrateddynamics.general.id\": \"§e§lID: §r%s\",\n    \"item.integrateddynamics.wrench\": \"Klíč\",\n    \"item.integrateddynamics.wrench.mode.default\": \"Výchozí\",\n    \"item.integrateddynamics.wrench.mode.default.info\": \"Odstranit části nebo otočit bloky\",\n    \"item.integrateddynamics.wrench.mode.offset.pos\": \"Pozice: %s\",\n    \"item.integrateddynamics.variable\": \"Proměnná karta\",\n    \"item.integrateddynamics.labeller.button.write\": \"Zapsat\",\n    \"item.integrateddynamics.facade\": \"Fasáda\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"Kbelík tekutého chorusu\",\n    \"fluid_type.integrateddynamics.liquid_chorus\": \"Tekutý chorus\",\n    \"biome.integrateddynamics.meneglin\": \"Menrilovníkový háj\",\n    \"operator.integrateddynamics.string.string_error\": \"Chyba\",\n    \"operator.integrateddynamics.string.string_error.info\": \"Vyhodí vlastní chybu\",\n    \"valuetype.integrateddynamics.boolean\": \"Boolean\",\n    \"valuetype.integrateddynamics.integer\": \"Celé číslo\",\n    \"valuetype.integrateddynamics.double\": \"Dvojitá\",\n    \"valuetype.integrateddynamics.long\": \"Dlouhá\",\n    \"valuetype.integrateddynamics.string\": \"Řetězec\",\n    \"valuetype.integrateddynamics.operator\": \"Operátor\",\n    \"valuetype.integrateddynamics.nbt\": \"NBT\",\n    \"valuetype.integrateddynamics.list\": \"Seznam\",\n    \"valuetype.integrateddynamics.block\": \"Blok\",\n    \"valuetype.integrateddynamics.itemstack\": \"Předmět\",\n    \"valuetype.integrateddynamics.entity\": \"Entita\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Tekutina\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/da_dk.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/de_de.json",
    "content": "{\n    \"itemGroup.integrateddynamics\": \"Integrated Dynamics\",\n    \"gui.integrateddynamics.button.save\": \"Speichern\",\n    \"gui.integrateddynamics.button.rename\": \"Umbenennen\",\n    \"gui.integrateddynamics.button.copy\": \"Kopieren\",\n    \"gui.integrateddynamics.button.copy.info\": \"In die Zwischenablage kopieren\",\n    \"gui.integrateddynamics.button.edit\": \"Bearbeiten\",\n    \"gui.integrateddynamics.button.add\": \"Hinzufügen\",\n    \"gui.integrateddynamics.button.remove\": \"Entfernen\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"Der Netzwerk-Energieverbrauch ist auf diesem Server deaktiviert.\",\n    \"gui.integrateddynamics.logicprogrammer.info.modify\": \"Variablenkarte §lmodifizieren§r\",\n    \"gui.integrateddynamics.rewards\": \"Belohnungen\",\n    \"gui.integrateddynamics.input\": \"Eingabe: %s\",\n    \"gui.integrateddynamics.output\": \"Ausgabe: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"Flüssigkeit automatisch auswerfen\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Flüssigkeit automatisch auswerfen: %s\",\n    \"gui.integrateddynamics.input_short\": \"Eingabe\",\n    \"gui.integrateddynamics.output_short\": \"Ausgabe\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Integrated Dynamics - Netzwerkdiagnose\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Netzwerk\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Kabel\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Tick-Zeit (ms)\",\n    \"key.categories.integrateddynamics\": \"Integrated Dynamics\",\n    \"block.integrateddynamics.coal_generator\": \"Generator\",\n    \"block.integrateddynamics.coal_generator.info\": \"Erzeugt Energie von Kraftstoffgegenständen\",\n    \"block.integrateddynamics.menril_log\": \"Menrilstamm\",\n    \"block.integrateddynamics.menril_log_stripped\": \"Entrindeter Menrilstamm\",\n    \"block.integrateddynamics.menril_leaves\": \"Menrillaub\",\n    \"block.integrateddynamics.menril_sapling\": \"Menrilsetzling\",\n    \"block.integrateddynamics.menril_planks\": \"Menrilholzbretter\",\n    \"block.integrateddynamics.menril_slab\": \"Menrilholzstufen\",\n    \"block.integrateddynamics.menril_fence\": \"Menrilholzzaun\",\n    \"block.integrateddynamics.menril_fence_gate\": \"Menrilholzzauntor\",\n    \"block.integrateddynamics.menril_wood\": \"Menrilholz\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"Entrindetes Menrilholz\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Kristallisierter Menrilblock\",\n    \"block.integrateddynamics.menril_torch\": \"Menrilfackel\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Menrilsteinfackel\",\n    \"block.integrateddynamics.invisible_light\": \"Unsichtbares Licht\",\n    \"block.integrateddynamics.menril_door\": \"Menriltür\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Menriltreppen\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Kristallisierte Menrilstufen\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"Kristallisierte Menrilstufe\",\n    \"item.integrateddynamics.variable\": \"Variablenkarte\",\n    \"item.integrateddynamics.labeller\": \"Etikettierer\",\n    \"item.integrateddynamics.labeller.info\": \"Beschriftet deine Variablen. Funktioniert auch im Logikprogrammierer.\",\n    \"item.integrateddynamics.facade.info\": \"Kombiniere mit einem Block auf einer Werkbank\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"Tragbarer Logikprogrammierer\",\n    \"advancement.integrateddynamics.menril_production\": \"Menrilproduktion\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"Bau ein Logikkabel\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"Bau ein Logiknetzwerk mit mindestens 10 verbundenen Logikkabeln.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"Menrilschrauberei\",\n    \"advancement.integrateddynamics.value_displaying\": \"Wertanzeige\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"Redstoneübertragung\",\n    \"advancement.integrateddynamics.lectern_data\": \"Lesepult-NBT\",\n    \"advancement.integrateddynamics.lectern_book\": \"Lesepult-NBT-Buch\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"Lesepult-NBT-Buchname\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"Arithmetische Addition\",\n    \"advancement.integrateddynamics.creeper_taming\": \"Creeperzähmung\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Flüssigkeit\",\n    \"valuetype.integrateddynamics.ingredients\": \"Komponenten\",\n    \"valuetype.integrateddynamics.recipe\": \"Rezept\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"Inaktiv\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"Fehler:\",\n    \"parttype.integrateddynamics.audio_reader\": \"Audioleser\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"Liest Audioinformationen\",\n    \"parttype.integrateddynamics.block_reader\": \"Blockleser\",\n    \"parttype.integrateddynamics.block_reader.info\": \"Liest Blockinformationen\",\n    \"parttype.integrateddynamics.entity_reader\": \"Objektleser\",\n    \"parttype.integrateddynamics.redstone_reader\": \"Redstoneleser\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"Liest Redstone-Werte\",\n    \"parttype.integrateddynamics.world_reader\": \"Weltleser\",\n    \"parttype.integrateddynamics.audio_writer\": \"Audioschreiber\",\n    \"parttype.integrateddynamics.effect_writer\": \"Effektschreiber\",\n    \"parttype.integrateddynamics.entity_writer\": \"Objektschreiber\",\n    \"parttype.integrateddynamics.machine_writer\": \"Maschinenschreiber\",\n    \"parttype.integrateddynamics.inventory_writer\": \"Inventarschreiber\",\n    \"parttype.integrateddynamics.redstone_writer\": \"Redstoneschreiber\",\n    \"parttype.integrateddynamics.static_light_panel\": \"Statisches Lichtpaneel\",\n    \"parttype.integrateddynamics.static_light_panel.info\": \"Ein einfaches Lichtpaneel, was immer aktiv ist\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"Dynamisches Lichtpaneel\",\n    \"parttype.integrateddynamics.display_panel\": \"Anzeigepaneel\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp\": \"Harfenote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp.info\": \"Liest eine Harfenote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum\": \"Große-Trommel-Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum.info\": \"Liest eine Große-Trommel-Note, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"Kleine-Trommel-Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"Liest eine Kleine-Trommel-Note, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat\": \"Beckennote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat.info\": \"Liest eine Beckennote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass\": \"Kontrabassnote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass.info\": \"Liest eine Kontrabassnote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute\": \"Flötennote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute.info\": \"Liest eine Flötennote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell\": \"Glöckchennote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell.info\": \"Liest eine Glöckchennote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar\": \"Gitarrennote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar.info\": \"Liest eine Gitarrennote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie\": \"Zombienote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie.info\": \"Liest eine Zombienote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton\": \"Skelettnote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton.info\": \"Liest eine Skelettnote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper\": \"Creepernote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper.info\": \"Liest eine Creepernote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon\": \"Drachennote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon.info\": \"Liest eine Drachennote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton\": \"Witherskelettnote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton.info\": \"Liest eine Witherskelettnote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin\": \"Piglinnote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin.info\": \"Liest eine Piglinnote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head\": \"Kopfnote\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head.info\": \"Liest eine Kopfnote, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp\": \"Harfenote\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp.info\": \"Gibt eine Harfenote aus, erwarteter Bereich [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum\": \"Große-Trommel-Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"Kleine-Trommel-Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat\": \"Beckennote\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/el_gr.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/en_us.json",
    "content": "{\n    \"_comment\": \"General\",\n    \"itemGroup.integrateddynamics\": \"Integrated Dynamics\",\n    \"general.integrateddynamics.description\": \"Take full and automated control of your appliances.\",\n    \"general.integrateddynamics.info.none\": \"None\",\n    \"general.integrateddynamics.energy\": \"Energy\",\n    \"general.integrateddynamics.energy_unit\": \"RF\",\n    \"general.integrateddynamics.error.no_network\": \"No valid network found\",\n    \"general.integrateddynamics.true\": \"True\",\n    \"general.integrateddynamics.false\": \"False\",\n\n    \"_comment\": \"Gui\",\n    \"gui.integrateddynamics.part_settings\": \"Part Settings\",\n    \"gui.integrateddynamics.aspect_settings\": \"Aspect Settings\",\n    \"gui.integrateddynamics.part_offsets\": \"Part Offsets\",\n    \"gui.integrateddynamics.keybinding\": \"Key Binding\",\n    \"gui.integrateddynamics.button.save\": \"Save\",\n    \"gui.integrateddynamics.button.rename\": \"Rename\",\n    \"gui.integrateddynamics.button.copy\": \"Copy\",\n    \"gui.integrateddynamics.button.copy.info\": \"Copy to clipboard\",\n    \"gui.integrateddynamics.button.edit\": \"Edit\",\n    \"gui.integrateddynamics.button.add\": \"Add\",\n    \"gui.integrateddynamics.button.remove\": \"Remove\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"Ticks/Operation\",\n    \"gui.integrateddynamics.partsettings.priority\": \"Priority\",\n    \"gui.integrateddynamics.partsettings.channel\": \"Energy Channel\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"Network energy consumption is disabled on this server.\",\n    \"gui.integrateddynamics.partsettings.side\": \"Target Side\",\n    \"gui.integrateddynamics.partoffset.offsets\": \"Offset relative to the target position\",\n    \"gui.integrateddynamics.partoffset.offsets.max\": \"Maximum offset: %s\",\n    \"gui.integrateddynamics.partoffset.offsets.max.howtoincrease\": \"Increase limit with Part Enhancement: Offset\",\n    \"gui.integrateddynamics.partoffset.slot.message.noint\": \"Invalid integer coordinate\",\n    \"gui.integrateddynamics.partoffset.slot.message.outofrange\": \"Offset exceeds the allowed range\",\n    \"gui.integrateddynamics.partoffset.x\": \"X Offset\",\n    \"gui.integrateddynamics.partoffset.y\": \"Y Offset\",\n    \"gui.integrateddynamics.partoffset.z\": \"Z Offset\",\n    \"gui.integrateddynamics.delay.capacity\": \"Capacity\",\n    \"gui.integrateddynamics.part.properties\": \"Properties:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"Filter\",\n    \"gui.integrateddynamics.logicprogrammer.reset\": \"Clear\",\n    \"gui.integrateddynamics.logicprogrammer.info.create\": \"§lBind new§r Variable Card\",\n    \"gui.integrateddynamics.logicprogrammer.info.modify\": \"§lModify§r Variable Card\",\n    \"gui.integrateddynamics.logicprogrammer.tooltip.writeslot.create\": \"Insert Variable Card to bind to a §lnew§r value\",\n    \"gui.integrateddynamics.logicprogrammer.tooltip.writeslot.modify\": \"Insert Variable Card to §lload§r its value\",\n    \"gui.integrateddynamics.rewards\": \"Rewards\",\n    \"gui.integrateddynamics.rewards.collect\": \"Collect\",\n    \"gui.integrateddynamics.rewards.collected\": \"Collected\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"Can't collect rewards, is your inventory full?\",\n    \"gui.integrateddynamics.input\": \"Input: %s\",\n    \"gui.integrateddynamics.output\": \"Output: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"Auto-eject Fluid\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Auto-eject Fluid: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"Push to adjacent tanks.\",\n    \"gui.integrateddynamics.input_short\": \"Input\",\n    \"gui.integrateddynamics.output_short\": \"Output\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"Strict NBT\",\n    \"gui.integrateddynamics.recipe.tagvariants\": \"Item tag variants\",\n    \"gui.integrateddynamics.recipe.reusable\": \"Reusable\",\n    \"gui.integrateddynamics.operator.globalname\": \"Global name: %s\",\n    \"gui.integrateddynamics.operator.localname\": \"Method name: %s\",\n\n    \"_comment\": \"Diagnostics\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Integrated Dynamics - Network Diagnostics\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"Parts\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"Storage Observers\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Network\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Cables\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"Part\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Ticktime (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"Dimension\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"Position\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"Side\",\n    \"gui.integrateddynamics.diagnostics.table.actions\": \"Actions\",\n\n    \"_comment\": \"Keybindings\",\n    \"key.categories.integrateddynamics\": \"Integrated Dynamics\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"Focus Search Logic Programmer\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"Rename Variable Logic Programmer\",\n\n    \"_comment\": \"Sound names\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"Flip Page\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"Flip Pages\",\n\n    \"_comment\": \"Blocks\",\n    \"block.integrateddynamics.cable\": \"Logic Cable\",\n    \"block.integrateddynamics.variablestore\": \"Variable Store\",\n    \"block.integrateddynamics.energy_battery\": \"Energy Battery\",\n    \"block.integrateddynamics.energy_battery.info\": \"Place in crafting grid with other Energy Batteries to increase capacity. Shift + Right click to auto-supply.\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"Auto-supply: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"Creative Energy Battery\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"Auto-supply: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"Logic Programmer\",\n    \"block.integrateddynamics.coal_generator\": \"Generator\",\n    \"block.integrateddynamics.coal_generator.info\": \"Produces energy from fuel items\",\n    \"block.integrateddynamics.proxy\": \"Proxy\",\n    \"block.integrateddynamics.proxy.info\": \"Expose a variable reference as an aspect. Clear in a crafting grid.\",\n    \"block.integrateddynamics.materializer\": \"Materializer\",\n    \"block.integrateddynamics.materializer.info\": \"Materialize the value of a variable in a new variable\",\n    \"block.integrateddynamics.menril_log\": \"Menril Log\",\n    \"block.integrateddynamics.menril_log_filled\": \"Enriched Menril Log\",\n    \"block.integrateddynamics.menril_log_stripped\": \"Stripped Menril Log\",\n    \"block.integrateddynamics.menril_leaves\": \"Menril Leaves\",\n    \"block.integrateddynamics.menril_sapling\": \"Menril Sapling\",\n    \"block.integrateddynamics.menril_planks\": \"Menril Planks\",\n    \"block.integrateddynamics.menril_slab\": \"Menril Slab\",\n    \"block.integrateddynamics.menril_fence\": \"Menril Fence\",\n    \"block.integrateddynamics.menril_fence_gate\": \"Menril Fence Gate\",\n    \"block.integrateddynamics.menril_wood\": \"Menril Wood\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"Stripped Menril Wood\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Block of Crystallized Menril\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"Crystallized Menril Brick\",\n    \"block.integrateddynamics.block_menril_resin\": \"Menril Resin\",\n    \"block.integrateddynamics.drying_basin\": \"Drying Basin\",\n    \"block.integrateddynamics.drying_basin.info\": \"For drying items and fluids\",\n    \"block.integrateddynamics.squeezer\": \"Squeezer\",\n    \"block.integrateddynamics.squeezer.info\": \"Squeezes, flattens and crushes by jumping on it. Redstone pulse to reset.\",\n    \"block.integrateddynamics.menril_torch\": \"Menril Torch\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Menril Stone Torch\",\n    \"block.integrateddynamics.invisible_light\": \"Invisible Light\",\n    \"block.integrateddynamics.menril_door\": \"Menril Door\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Menril Stairs\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Crystallized Menril Stairs\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"Crystallized Menril Brick Stairs\",\n    \"block.integrateddynamics.delay\": \"Delayer\",\n    \"block.integrateddynamics.delay.info\": \"Expose the history of a variable as an aspect. Clear in a crafting grid.\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"Liquid Chorus\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"Block of Crystallized Chorus\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"Crystallized Chorus Brick\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"Crystallized Chorus Stairs\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"Crystallized Chorus Brick Stairs\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"Mechanical Squeezer\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"Squeezes, flattens and crushes using energy.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"Mechanical Drying Basin\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"For drying items and fluids using energy.\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"Crystallized Menril Slab\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"Crystallized Menril Brick Slab\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"Crystallized Chorus Slab\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"Crystallized Chorus Brick Slab\",\n\n    \"_comment\": \"Items\",\n    \"item.integrateddynamics.general.id\": \"§e§lID: §r%s\",\n    \"item.integrateddynamics.wrench\": \"Wrench\",\n    \"item.integrateddynamics.wrench.info\": \"Shift + Right-click in air to change mode\",\n    \"item.integrateddynamics.wrench.mode\": \"§eMode: §r%s\",\n    \"item.integrateddynamics.wrench.mode.default\": \"Default\",\n    \"item.integrateddynamics.wrench.mode.default.info\": \"Remove parts or rotate blocks\",\n    \"item.integrateddynamics.wrench.mode.offset\": \"Save Offset\",\n    \"item.integrateddynamics.wrench.mode.offset.info\": \"Shift + Right-click on a position and Right-click on part to link offset\",\n    \"item.integrateddynamics.wrench.mode.offset.saved\": \"Position was saved in Wrench: %s\",\n    \"item.integrateddynamics.wrench.mode.offset.success\": \"New offset position has been set in part\",\n    \"item.integrateddynamics.wrench.mode.offset.incomplete\": \"No position was saved into this Wrench yet\",\n    \"item.integrateddynamics.wrench.mode.offset.fail\": \"Could not set offset. Increase max offset using Enhancements!\",\n    \"item.integrateddynamics.wrench.mode.offset.pos\": \"Position: %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side\": \"Save Offset and Side\",\n    \"item.integrateddynamics.wrench.mode.offset_side.info\": \"Shift + Right-click on a position and Right-click on part to link offset and side\",\n    \"item.integrateddynamics.wrench.mode.offset_side.saved\": \"Position and Side were saved in Wrench: %s - %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side.success\": \"New offset position and side have been set in part\",\n    \"item.integrateddynamics.wrench.mode.offset_side.side\": \"Side: %s\",\n    \"item.integrateddynamics.variable\": \"Variable Card\",\n    \"item.integrateddynamics.variable.info\": \"Clear or copy in a crafting grid\",\n    \"item.integrateddynamics.variable.warning\": \"§4§lWARNING: Do NOT copy this item by middle-clicking!\",\n    \"item.integrateddynamics.variable.id\": \"§e§oVariable ID: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"Labeller\",\n    \"item.integrateddynamics.labeller.info\": \"Labels your variables. Also works in the Logic Programmer.\",\n    \"item.integrateddynamics.labeller.button.write\": \"Write\",\n    \"item.integrateddynamics.facade\": \"Facade\",\n    \"item.integrateddynamics.facade.info\": \"Combine with a block in crafting table\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"Liquid Chorus Bucket\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"Menril Resin Bucket\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"Crystallized Menril Chunk\",\n    \"item.integrateddynamics.variable_transformer\": \"Input/Output Variable Transformers\",\n    \"item.integrateddynamics.variable_transformer_output\": \"Output Variable Transformer\",\n    \"item.integrateddynamics.variable_transformer_input\": \"Input Variable Transformer\",\n    \"item.integrateddynamics.menril_berries\": \"Menril Berries\",\n    \"item.integrateddynamics.menril_berries.info\": \"Tasty and illuminating\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"Portable Logic Programmer\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"On the Dynamics of Integration\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"A complete mod reference guide with tutorials\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"Crystallized Chorus Chunk\",\n    \"item.integrateddynamics.logic_director\": \"Logic Director\",\n    \"item.integrateddynamics.proto_chorus\": \"Proto Chorus\",\n    \"item.integrateddynamics.enhancement_offset\": \"Part Enhancement: Offset\",\n    \"item.integrateddynamics.enhancement_offset.info\": \"Increases the max offset of a part by right-clicking\",\n    \"item.integrateddynamics.enhancement_offset.tooltip\": \"Offset increase: %s\",\n    \"item.integrateddynamics.enhancement_offset.increased\": \"Part offset increased to %s\",\n    \"item.integrateddynamics.enhancement_offset.limit\": \"Part offset is already at the limit (%s)\",\n\n    \"_comment\": \"Fluids\",\n    \"fluid_type.integrateddynamics.menril_resin\": \"Menril Resin\",\n    \"fluid_type.integrateddynamics.liquid_chorus\": \"Liquid Chorus\",\n\n    \"_comment\": \"Biomes\",\n    \"biome.integrateddynamics.meneglin\": \"Meneglin\",\n\n    \"_comment\": \"Advancements\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"Meneglin Discovery\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"Chop down a Menril tree, which can most easily be found in Meneglin biomes.\",\n    \"advancement.integrateddynamics.squeezing\": \"Squeezing like a boss\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"Craft a Squeezer.\",\n    \"advancement.integrateddynamics.drying\": \"Drying like a boss\",\n    \"advancement.integrateddynamics.drying.desc\": \"Craft a Drying Basin.\",\n    \"advancement.integrateddynamics.menril_production\": \"Menril Production\",\n    \"advancement.integrateddynamics.menril_production.desc\": \"Process Menril Log into Menril Resin and dry it to obtain Crystallized Menril.\",\n    \"advancement.integrateddynamics.cables_logic\": \"Cables, for logic\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"Craft a Logic Cable\",\n    \"advancement.integrateddynamics.networks_logic\": \"Networks, for logic\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"Build a logic network with at least 10 connected Logic Cables.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"Menril Wrenching\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"Craft a Wrench\",\n    \"advancement.integrateddynamics.variables\": \"Variables, For Values\",\n    \"advancement.integrateddynamics.variables.desc\": \"Craft a Variable Card\",\n    \"advancement.integrateddynamics.variable_input\": \"Variable Input Transformation\",\n    \"advancement.integrateddynamics.variable_input.desc\": \"Craft a Variable Input Transformer\",\n    \"advancement.integrateddynamics.variable_output\": \"Variable Output Transformation\",\n    \"advancement.integrateddynamics.variable_output.desc\": \"Craft a Variable Output Transformer\",\n    \"advancement.integrateddynamics.redstone_reading\": \"Redstone Reading\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"Craft a Redstone Reader\",\n    \"advancement.integrateddynamics.block_reading\": \"Block Reading\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"Craft a Block Reader\",\n    \"advancement.integrateddynamics.inventory_reading\": \"Inventory Reading\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"Craft an Inventory Reader\",\n    \"advancement.integrateddynamics.machine_reading\": \"Machine Reading\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"Craft a Machine Reader\",\n    \"advancement.integrateddynamics.value_displaying\": \"Value Displaying\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"Craft a Display Panel\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"Redstone Capturing\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"Make a variable card for a redstone §6Integer§7 value.\",\n    \"advancement.integrateddynamics.redstone_observement\": \"Redstone Observement\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"Place an §6Integer§7 variable card in a Display Panel.\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"Redstone Transmission\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"Let a Redstone Writer write a maximal integer redstone level that was read by a Redstone Reader.\",\n    \"advancement.integrateddynamics.recipe_reading\": \"Recipe Reading\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"Read the list of recipes from a machine (like the Crafting Table) using a Machine Reader.\",\n    \"advancement.integrateddynamics.recipe_creation\": \"Recipe Creation\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"Visually create a §8Recipe§7 using the Logic Programmer.\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"Recipe Lookup\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"Dynamically lookup the recipe of a Chest using a Machine Reader.\",\n    \"advancement.integrateddynamics.lectern_data\": \"Lectern NBT\",\n    \"advancement.integrateddynamics.lectern_data.desc\": \"Read the NBT tag from a machine using a Block Reader, such as the Lectern.\",\n    \"advancement.integrateddynamics.lectern_book\": \"Lectern NBT Book\",\n    \"advancement.integrateddynamics.lectern_book.desc\": \"Read the Book NBT field inside a Lectern block on the NBT tag read from a Block Reader.\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"Lectern NBT Book Name\",\n    \"advancement.integrateddynamics.lectern_book_name.desc\": \"Read the name (id) of a book inside a Lectern block by applying an NBT Path expression on the NBT tag read from a Block Reader.\",\n\n    \"advancement.integrateddynamics.logic_programming\": \"Logic Programming\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"Open a Logic Programmer's GUI\",\n    \"advancement.integrateddynamics.constant_definition\": \"Constant Definition\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"Define a constant §6Integer§7 Variable Card.\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"Arithmetic Addition\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"Add two §eNumbers§7 and display the result.\",\n    \"advancement.integrateddynamics.variable_materialization\": \"Variable Materialization\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"Materialize a variable in a §lMaterializer§r.\",\n    \"advancement.integrateddynamics.variable_proxying\": \"Variable Proxying\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"Create a valid proxy variable in a §lProxy§r.\",\n    \"advancement.integrateddynamics.logical_list_building\": \"Logical List Building\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"Construct a static List with §6Integers§7 1, 10 and 100.\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"Item Origin Identification\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"Get the mod name of an Item.\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"What Would I Be Looking At?\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"Get the Block you are looking at.\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"Dynamic Additions\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"Invoke the addition Operator dynamically\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"Dynamic List Filtering\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"Filter an Integer list 1, 10 and 100 to 10.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"Creeper Taming\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"Show the HP of a Creeper in a Display Panel\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"Sponge Step Sound\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"Play the step sound of a Wet Sponge block\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"Recursive Recursion\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"Create an infinite List, and display it in a Display Panel\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"Tools for Obsidian?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"Check if a Stone Pickaxe can break an Obsidian block, and show the result in a Display Panel.\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"Smart Pressure Plate\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"Create a system that automatically opens a door when you pass by an Entity Reader based on entity name matching, using a Redstone Writer directly pointing to the door. (Hint: pass an entity name as first argument into the equals operator)\",\n\n    \"_comment\": \"Value types\",\n    \"valuetype.integrateddynamics.error.value\": \"Invalid value\",\n    \"valuetype.integrateddynamics.value_type\": \"Value Type\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§eType: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§oValue: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§eSignature: §r\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"The input \\\"%s\\\" could not be parsed for this value type.\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"This value type requires an input item.\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"Element %s is invalid: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"The list expects value type %s while %s was given.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"An operator value type was expected at position %s, while %s was given\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"The operator value %s expected value types (%s), while (%s) was given.\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"The inserted item is no block.\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"The inserted item contains no fluid.\",\n    \"valuetype.integrateddynamics.error.no_light_calculator\": \"The value type %s can not be used to derive a light level.\",\n    \"valuetype.integrateddynamics.error.deserialize\": \"Something went wrong while deserializing '%s': %s\",\n\n    \"valuetype.integrateddynamics.any\": \"Any\",\n    \"valuetype.integrateddynamics.any.info\": \"Accepts any type of value.\",\n    \"valuetype.integrateddynamics.number\": \"Number\",\n    \"valuetype.integrateddynamics.number.info\": \"Accepts all number types.\",\n    \"valuetype.integrateddynamics.named\": \"Named\",\n    \"valuetype.integrateddynamics.named.info\": \"Something that has a name, such as display names of items and blocks.\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"Uniquely Named\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"Something that has a unique name, such as items and blocks.\",\n    \"operator.integrateddynamics.string.string_error\": \"Error\",\n    \"operator.integrateddynamics.string.string_error.info\": \"Throw a custom error\", \n\n\n    \"valuetype.integrateddynamics.boolean\": \"Boolean\",\n    \"valuetype.integrateddynamics.integer\": \"Integer\",\n    \"valuetype.integrateddynamics.double\": \"Double\",\n    \"valuetype.integrateddynamics.long\": \"Long\",\n    \"valuetype.integrateddynamics.string\": \"String\",\n    \"valuetype.integrateddynamics.operator\": \"Operator\",\n    \"valuetype.integrateddynamics.nbt\": \"NBT\",\n\n    \"valuetype.integrateddynamics.list\": \"List\",\n    \"valuetype.integrateddynamics.block\": \"Block\",\n    \"valuetype.integrateddynamics.itemstack\": \"Item\",\n    \"valuetype.integrateddynamics.entity\": \"Entity\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Fluid\",\n    \"valuetype.integrateddynamics.ingredients\": \"Ingredients\",\n    \"valuetype.integrateddynamics.recipe\": \"Recipe\",\n\n    \"valuetype.integrateddynamics.ingredients.slot.info\": \"Shift click to configure\",\n\n    \"_comment\": \"Part types\",\n    \"parttype.integrateddynamics.error.low_energy\": \"Not enough energy\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"Out of Energy\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"Inactive\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"Errors:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"Aspect: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"Value: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"Group: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"WARNING: No aspects available. This may become usable when other mods are installed.\",\n    \"parttype.integrateddynamics.tooltip.maxoffset\": \"Max Offset: %s\",\n\n    \"parttype.integrateddynamics.audio_reader\": \"Audio Reader\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"Reads audio information\",\n    \"parttype.integrateddynamics.block_reader\": \"Block Reader\",\n    \"parttype.integrateddynamics.block_reader.info\": \"Reads block information\",\n    \"parttype.integrateddynamics.entity_reader\": \"Entity Reader\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"Reads entity information\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"Extra-Dimensional Reader\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"Reads information that is shared for all dimensions\",\n    \"parttype.integrateddynamics.fluid_reader\": \"Fluid Reader\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"Reads fluid tanks\",\n    \"parttype.integrateddynamics.inventory_reader\": \"Inventory Reader\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"Reads data from inventories\",\n    \"parttype.integrateddynamics.network_reader\": \"Network Reader\",\n    \"parttype.integrateddynamics.network_reader.info\": \"Reads target network properties\",\n    \"parttype.integrateddynamics.machine_reader\": \"Machine Reader\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"Reads machine properties\",\n    \"parttype.integrateddynamics.redstone_reader\": \"Redstone Reader\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"Reads redstone values\",\n    \"parttype.integrateddynamics.world_reader\": \"World Reader\",\n    \"parttype.integrateddynamics.world_reader.info\": \"Reads things related to the world, blocks and entities\",\n\n    \"parttype.integrateddynamics.audio_writer\": \"Audio Writer\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"Outputs audio and music\",\n    \"parttype.integrateddynamics.effect_writer\": \"Effect Writer\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"Outputs particles\",\n    \"parttype.integrateddynamics.entity_writer\": \"Entity Writer\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"Writes entity configuration\",\n    \"parttype.integrateddynamics.machine_writer\": \"Machine Writer\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"Writes machine configuration\",\n    \"parttype.integrateddynamics.inventory_writer\": \"Inventory Writer\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"Writes to inventories\",\n    \"parttype.integrateddynamics.redstone_writer\": \"Redstone Writer\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"Writes to redstone\",\n\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"This panel does not accept a %s.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"Static Light Panel\",\n    \"parttype.integrateddynamics.static_light_panel.info\": \"A simple light panel that is always active\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"Dynamic Light Panel\",\n    \"parttype.integrateddynamics.dynamic_light_panel.info\": \"Visually display a variable value in the world as a light value\",\n    \"parttype.integrateddynamics.display_panel\": \"Display Panel\",\n    \"parttype.integrateddynamics.display_panel.info\": \"Visually display a variable value in the world as text\",\n\n    \"parttype.integrateddynamics.connector_mono_directional\": \"Mono-Directional Connector\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"Can connect to other Mono-Directional Connectors over a distance in a straight line\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"Omni-Directional Connector\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"Can connect to other Omni-Directional Connectors anywhere, including different dimensions.\",\n\n    \"_comment\": \"Variables facades\",\n    \"variable.integrateddynamics.error.invalid_item\": \"The current variable can not be used in this aspect.\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"Part with id %s could not be found within the current network.\",\n    \"variable.integrateddynamics.error.recursion\": \"The variable with id %s is referring to itself.\",\n\n    \"_comment\": \"Proxy\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§oProxy ID: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"Proxy with id %s could not be found within the current network.\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"Proxy with id %s is not exposing a variable.\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"Proxy with id %s is expected to expose a variable with type %s while %s is found.\",\n\n    \"_comment\": \"Delay\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§oDelay ID: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"Delay with id %s could not be found within the current network.\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"Delay with id %s is not exposing a variable.\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"Delay with id %s is expected to expose a variable with type %s while %s is found.\",\n\n    \"_comment\": \"Aspects\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§eAspect: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§eType: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§oPart ID: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"This aspect expects %s, but %s was given.\",\n    \"aspect.integrateddynamics.error.recursion\": \"The aspect variable for %s is referring to itself.\",\n    \"aspect.integrateddynamics.error.no_value_interface\": \"No valid value interface was found.\",\n    \"aspect.integrateddynamics.error.no_value_interface_value\": \"No valid value interface value was found.\",\n    \"aspect.integrateddynamics.name\": \"Aspect\",\n\n    \"_comment\": \"# Audio aspects\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp\": \"Harp Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp.info\": \"Reads a harp note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum\": \"Base Drum Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum.info\": \"Reads a base drum note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"Snare Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"Reads a snare note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat\": \"Hat Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat.info\": \"Reads a hat note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass\": \"Bass Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass.info\": \"Reads a bass note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute\": \"Flute Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute.info\": \"Reads a flute note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell\": \"Bell Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell.info\": \"Reads a bell note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar\": \"Guitar Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar.info\": \"Reads a guitar note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime\": \"Chime Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime.info\": \"Reads a chime note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone\": \"Xylophone Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone.info\": \"Reads a xylophone note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone\": \"Iron Xylophone Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone.info\": \"Reads an iron xylophone note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell\": \"Cow Bell Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell.info\": \"Reads a cow bell note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo\": \"Didgeridoo Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo.info\": \"Reads a didgeridoo note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit\": \"Bit Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit.info\": \"Reads a bit note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo\": \"Banjo Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo.info\": \"Reads a banjo note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling\": \"Pling Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling.info\": \"Reads a pling note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie\": \"Zombie Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie.info\": \"Reads a zombie note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton\": \"Skeleton Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton.info\": \"Reads a skeleton note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper\": \"Creeper Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper.info\": \"Reads a creeper note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon\": \"Dragon Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon.info\": \"Reads a dragon note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton\": \"Wither Skeleton Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton.info\": \"Reads a wither skeleton note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin\": \"Piglin Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin.info\": \"Reads a piglin note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head\": \"Head Note\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head.info\": \"Reads a head note, expected range [0, 24]\",\n\n\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp\": \"Harp Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp.info\": \"Output a harp note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum\": \"Base Drum Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum.info\": \"Output a base drum note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"Snare Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"Output a snare note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat\": \"Hat Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat.info\": \"Output a hat note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass\": \"Bass Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass.info\": \"Output a bass note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute\": \"Flute Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute.info\": \"Output a flute note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell\": \"Bell Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell.info\": \"Output a bell note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar\": \"Guitar Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar.info\": \"Output a guitar note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime\": \"Chime Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime.info\": \"Output a chime note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone\": \"Xylophone Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone.info\": \"Output a xylophone note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone\": \"Iron Xylophone Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone.info\": \"Output an iron xylophone note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell\": \"Cow Bell Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell.info\": \"Output a cow bell note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo\": \"Didgeridoo Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo.info\": \"Output a didgeridoo note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit\": \"Bit Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit.info\": \"Output a bit note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo\": \"Banjo Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo.info\": \"Output a banjo note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling\": \"Pling Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling.info\": \"Output a pling note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.zombie\": \"Zombie Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.zombie.info\": \"Output a zombie note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.skeleton\": \"Skeleton Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.skeleton.info\": \"Output a skeleton note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.creeper\": \"Creeper Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.creeper.info\": \"Output a creeper note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.dragon\": \"Dragon Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.dragon.info\": \"Output a dragon note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.wither_skeleton\": \"Wither Skeleton Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.wither_skeleton.info\": \"Output a wither skeleton note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.piglin\": \"Piglin Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.piglin.info\": \"Output a piglin note, expected range [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.custom_head\": \"Head Note\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.custom_head.info\": \"Output a head note, expected range [0, 24]\",\n\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"Sound\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"Play the given sound name\",\n    \"aspect.integrateddynamics.write.string.audio.text\": \"Text\",\n    \"aspect.integrateddynamics.write.string.audio.text.info\": \"Convert the given text to speech\",\n\n    \"_comment\": \"# Effect aspects\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager\": \"Particle: Angry Villager\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager.info\": \"Emit an angry villager particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble\": \"Particle: Bubble\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble.info\": \"Emit a bubble particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud\": \"Particle: Cloud\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud.info\": \"Emit a cloud particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit\": \"Particle: Crit\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit.info\": \"Emit a crit particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator\": \"Particle: Damage Indicator\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator.info\": \"Emit a damage indicator particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath\": \"Particle: Dragon Breath\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath.info\": \"Emit a dragon breath particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava\": \"Particle: Dripping Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava.info\": \"Emit a dripping lava particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava\": \"Particle: Falling Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava.info\": \"Emit a falling lava particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava\": \"Particle: Landing Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava.info\": \"Emit a landing lava particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water\": \"Particle: Dripping Water\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water.info\": \"Emit a dripping water particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water\": \"Particle: Falling Water\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water.info\": \"Emit a falling water particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect\": \"Particle: Effect\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect.info\": \"Emit an effect particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian\": \"Particle: Elder Guardian\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian.info\": \"Emit an elder guardian particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit\": \"Particle: Enchanted Hit\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit.info\": \"Emit an enchanted hit particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant\": \"Particle: Enchanted\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant.info\": \"Emit an enchanted particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod\": \"Particle: End Rod\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod.info\": \"Emit an end rod particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter\": \"Particle: Explosion Emitter\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter.info\": \"Emit an explosion emitter particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion\": \"Particle: Explosion\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion.info\": \"Emit an explosion particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sonic_boom\": \"Particle: Sonic Boom\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sonic_boom.info\": \"Emit a sonic boom particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework\": \"Particle: Firework\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework.info\": \"Emit a firework particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing\": \"Particle: Fishing\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing.info\": \"Emit a fishing particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame\": \"Particle: Flame\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame.info\": \"Emit a flame particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame\": \"Particle: Soul Fire Flame\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame.info\": \"Emit a Soul Fire Flame particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul\": \"Particle: Sculk Soul\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul.info\": \"Emit a Sculk Soul particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop\": \"Particle: Sculk Charge Pop\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop.info\": \"Emit a Sculk Charge Pop particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul\": \"Particle: Soul\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul.info\": \"Emit a Soul particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash\": \"Particle: Flash\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash.info\": \"Emit a Flash particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager\": \"Particle: Happy Villager\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager.info\": \"Emit a happy villager particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter\": \"Particle: Composter\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter.info\": \"Emit a composter particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart\": \"Particle: Heart\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart.info\": \"Emit a heart particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect\": \"Particle: Instant Effect\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect.info\": \"Emit an instant effect particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime\": \"Particle: Slime\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime.info\": \"Emit a slime particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball\": \"Particle: Snowball\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball.info\": \"Emit a snowball particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke\": \"Particle: Large Smoke\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke.info\": \"Emit a large smoke particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava\": \"Particle: Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava.info\": \"Emit a lava particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium\": \"Particle: Mycelium\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium.info\": \"Emit a mycelium particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note\": \"Particle: Note\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note.info\": \"Emit a note particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof\": \"Particle: Poof\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof.info\": \"Emit a poof particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal\": \"Particle: Portal\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal.info\": \"Emit a portal particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain\": \"Particle: Rain\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain.info\": \"Emit a rain particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke\": \"Particle: Smoke\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke.info\": \"Emit a smoke particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze\": \"Particle: Sneeze\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze.info\": \"Emit a sneeze particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit\": \"Particle: Spit\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit.info\": \"Emit a spit particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink\": \"Particle: Squid Ink\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink.info\": \"Emit a squid ink particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack\": \"Particle: Sweep Attack\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack.info\": \"Emit a sweep attack particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying\": \"Particle: Totem of Undying\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying.info\": \"Emit a totem of undying particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater\": \"Particle: Underwater\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater.info\": \"Emit an underwater particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash\": \"Particle: Splash\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash.info\": \"Emit a splash particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch\": \"Particle: Witch\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch.info\": \"Emit a witch particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop\": \"Particle: Bubble Pop\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop.info\": \"Emit a bubble pop particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down\": \"Particle: Downwards Current\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down.info\": \"Emit a downwards current particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up\": \"Particle: Upwards Current\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up.info\": \"Emit an upwards current particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus\": \"Particle: Nautilus\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus.info\": \"Emit a nautilus particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin\": \"Particle: Dolphin\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin.info\": \"Emit a dolphin particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke\": \"Particle: Cosy Campfire Smoke\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke.info\": \"Emit a cosy campfire smoke particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke\": \"Particle: Signal Campfire Smoke\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke.info\": \"Emit a signal campfire smoke particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey\": \"Particle: Dripping Honey\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey.info\": \"Emit a dripping honey particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey\": \"Particle: Falling Honey\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey.info\": \"Emit a falling honey particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey\": \"Particle: Landing Honey\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey.info\": \"Emit a landing honey particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar\": \"Particle: Falling Nectar\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar.info\": \"Emit a falling nectar particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom\": \"Particle: Falling Spore Blossom\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom.info\": \"Emit a falling spore blossom particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash\": \"Particle: Ash\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash.info\": \"Emit an ash particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore\": \"Particle: Crimson Spore\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore.info\": \"Emit a crimson spore particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore\": \"Particle: Warped Spore\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore.info\": \"Emit a warped spore particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air\": \"Particle: Spore Blossom Air\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air.info\": \"Emit a spore blossom air particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear\": \"Particle: Dripping Obsidian Tear\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear.info\": \"Emit a dripping obsidian tear particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear\": \"Particle: Falling Obsidian Tear\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear.info\": \"Emit a falling obsidian tear particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear\": \"Particle: Landing Obsidian Tear\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear.info\": \"Emit a landing obsidian tear particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal\": \"Particle: Reverse Portal\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal.info\": \"Emit a reverse portal particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash\": \"Particle: White Ash\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash.info\": \"Emit a white ash particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame\": \"Particle: Small Flame\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame.info\": \"Emit a small flame particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake\": \"Particle: Snowflake\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake.info\": \"Emit a snowflake particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava\": \"Particle: Dripping Dripstone Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava.info\": \"Emit a dripping dripstone lava particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava\": \"Particle: Falling Dripstone Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava.info\": \"Emit a falling dripstone lava particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water\": \"Particle: Dripping Dripstone Water\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water.info\": \"Emit a dripping dripstone water particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water\": \"Particle: Falling Dripstone Water\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water.info\": \"Emit a falling dripstone water particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink\": \"Particle: Glow Squid Ink\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink.info\": \"Emit a glow squid ink particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow\": \"Particle: Glow\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow.info\": \"Emit a glow particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on\": \"Particle: Wax On\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on.info\": \"Emit a wax on particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off\": \"Particle: Wax Off\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off.info\": \"Emit a wax off particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark\": \"Particle: Electric Spark\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark.info\": \"Emit an electric spark particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape\": \"Particle: Scrape\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape.info\": \"Emit a scrape particle with a certain velocity.\",\n\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_egg_crack\": \"Particle: Egg Crack\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_egg_crack.info\": \"Emit an egg crack particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dust_plume\": \"Particle: Dust Plume\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dust_plume.info\": \"Emit a dust plume particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player\": \"Particle: Trial Spawner Detected Player\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player.info\": \"Emit a trial spawner detected player particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player_ominous\": \"Particle: Trial Spawner Detected Player Ominous\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player_ominous.info\": \"Emit a trial spawner detected player ominous particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_vault_connection\": \"Particle: Vault Connection\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_vault_connection.info\": \"Emit a vault connection particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ominous_spawning\": \"Particle: Ominous Spawning\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ominous_spawning.info\": \"Emit an ominous spawning particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_raid_omen\": \"Particle: Raid Omen\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_raid_omen.info\": \"Emit a raid omen particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_omen\": \"Particle: Trial Omen\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_omen.info\": \"Emit a trial omen particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firefly\": \"Particle: Firefly\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firefly.info\": \"Emit a firefly particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_pause_mob_growth\": \"Particle: Pause Mob Growth\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_pause_mob_growth.info\": \"Emit a pause mob growth particle with a certain velocity.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reset_mob_growth\": \"Particle: Reset Mob Growth\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reset_mob_growth.info\": \"Emit a reset mob growth particle with a certain velocity.\",\n\n    \"_comment\": \"# Block aspects\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"Has Block\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"If the target space has a block\",\n\n    \"aspect.integrateddynamics.read.string.block.dimension\": \"Dimension\",\n    \"aspect.integrateddynamics.read.string.block.dimension.info\": \"The id of the target world dimension\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"X Coordinate\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"The X coordinate of the target\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Y Coordinate\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"The Y coordinate of the target\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Z Coordinate\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"The Z coordinate of the target\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"Tile Entity NBT\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"The target Tile Entity as NBT\",\n    \"aspect.integrateddynamics.read.string.block.biome\": \"Biome\",\n    \"aspect.integrateddynamics.read.string.block.biome.info\": \"The biome name at the target\",\n    \"aspect.integrateddynamics.read.integer.block.light\": \"Light Level\",\n    \"aspect.integrateddynamics.read.integer.block.light.info\": \"The light level at the target\",\n\n    \"aspect.integrateddynamics.read.block.block\": \"Block\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"The target block\",\n\n    \"_comment\": \"# Entity aspects\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"Item Frame Rotation\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"The index of the rotation the item in the attached Item Frame has.\",\n\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"Entities\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"The entities in the target space\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"Players\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"The players that are in the target space\",\n\n    \"aspect.integrateddynamics.read.entity.entity\": \"Entity\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"The selected entity in the target space\",\n\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"Item Frame\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"The item in an Item Frame attached to the reader\",\n\n    \"_comment\": \"# Extra-Dimensional aspects\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"Random\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"A random integer value\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"Player Count\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"The amount of players in the server\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"Tick time\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"The average tick time in nanoseconds for the server\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"The average number of ticks per second over all worlds\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"Players\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"The players that are online on this server\",\n\n    \"_comment\": \"# Fluid aspects\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"Tank Empty\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"If the target fluid tank is empty\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"Tank Full\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"If the target fluid tank is full\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"Tank Not Empty\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"If the target fluid tank has something in it\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"Is Tank\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"If the target has a fluid tank\",\n\n    \"aspect.integrateddynamics.read.long.fluid.amount\": \"Fluid Amount\",\n    \"aspect.integrateddynamics.read.long.fluid.amount.info\": \"The amount of fluid in mB in the active tank\",\n    \"aspect.integrateddynamics.read.long.fluid.totalamount\": \"Total Fluid Amount\",\n    \"aspect.integrateddynamics.read.long.fluid.totalamount.info\": \"The total amount of fluid in mB\",\n    \"aspect.integrateddynamics.read.long.fluid.capacity\": \"Fluid Capacity\",\n    \"aspect.integrateddynamics.read.long.fluid.capacity.info\": \"Total capacity of the active tank\",\n    \"aspect.integrateddynamics.read.long.fluid.totalcapacity\": \"Total Fluid Capacity\",\n    \"aspect.integrateddynamics.read.long.fluid.totalcapacity.info\": \"Total capacity of the tank\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"Tanks\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"The amount of tanks found at the target\",\n\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"Fluid Fill Ratio\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"The amount of fluid in the active tank divided by its capacity\",\n\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"Tank Fluids\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"The list of fluids in all tanks\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"Tank Capacities\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"The list of capacities of all tanks\",\n\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"Tank Fluid\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"The fluid in the target tank\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"Block Fluid\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"The fluid in the target block\",\n\n    \"_comment\": \"# Inventory aspects\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"Inventory Full\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"If all slots have at least one item\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"Inventory Empty\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"If all slots are empty\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"Inventory Not Empty\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"If there is at least one item\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"Is Inventory\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"If the target has an inventory\",\n\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"Inventory Count\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"Get the total amount of items\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"Slot Stacksize\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"Get the stack size in the active slot\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"Slots\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"The number of slots in the inventory\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"Slots Filled\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"The number of slots that have an item\",\n\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"Fill Ratio\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"The number of non-empty slots divided by the total number of slots\",\n\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"Items\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"Get a list of the items in the inventory\",\n\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"Slot Item\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"Get the Item in the active slot\",\n\n    \"_comment\": \"# Machine aspects\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"Is Worker\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"If the target is a worker machine\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"Has Work\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"If the target has work to process\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"Can Work\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"If the target would be able to start working in its current state\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"Is Working\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"If the target is currently working\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"Has Temperature\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"If the target has a certain temperature\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"Temperature\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature.info\": \"The current temperature of the target in Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"Maximum Temperature\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature.info\": \"The maximum temperature of the target in Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"Minimum Temperature\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature.info\": \"The minimum temperature of the target in Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"Default Temperature\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature.info\": \"The default temperature of the target in Kelvin\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"Is Recipe Handler\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"If the target can handle recipes\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"Recipes\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"Recipes that can be handled by the target\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"Recipe Output By Input\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"An operator that returns the resulting recipe output for a given input.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"Recipe Inputs By Output\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"An operator that returns all possible recipe inputs for a given output.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"Recipes By Input\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"An operator that returns all possible recipes for a given input.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"Recipes By Output\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"An operator that returns all possible recipes for a given output.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"Recipe By Input\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"An operator that returns a recipe for a given input.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"Recipe By Output\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"An operator that returns a recipe for a given output.\",\n    \"aspect.integrateddynamics.read.boolean.energy.applicable\": \"Is Energy Handler\",\n    \"aspect.integrateddynamics.read.boolean.energy.applicable.info\": \"If the target in some way handles energy\",\n    \"aspect.integrateddynamics.read.boolean.energy.canextract\": \"Can Extract Energy\",\n    \"aspect.integrateddynamics.read.boolean.energy.canextract.info\": \"If energy can really be extracted from the target, takes storage into account\",\n    \"aspect.integrateddynamics.read.boolean.energy.caninsert\": \"Can Insert Energy\",\n    \"aspect.integrateddynamics.read.boolean.energy.caninsert.info\": \"If energy can really be inserted into the target, takes storage and capacity into account\",\n    \"aspect.integrateddynamics.read.boolean.energy.isfull\": \"Is Energy Buffer Full\",\n    \"aspect.integrateddynamics.read.boolean.energy.isfull.info\": \"If the target's energy buffer is completely full\",\n    \"aspect.integrateddynamics.read.boolean.energy.isempty\": \"Is Energy Buffer Empty\",\n    \"aspect.integrateddynamics.read.boolean.energy.isempty.info\": \"If the target's energy buffer is completely empty\",\n    \"aspect.integrateddynamics.read.boolean.energy.isnonempty\": \"Is Energy Buffer Not Empty\",\n    \"aspect.integrateddynamics.read.boolean.energy.isnonempty.info\": \"If the target's energy buffer is not empty\",\n    \"aspect.integrateddynamics.read.long.energy.amount\": \"Stored Energy\",\n    \"aspect.integrateddynamics.read.long.energy.amount.info\": \"The amount of energy stored in the target\",\n    \"aspect.integrateddynamics.read.long.energy.capacity\": \"Energy Capacity\",\n    \"aspect.integrateddynamics.read.long.energy.capacity.info\": \"The energy capacity of the target\",\n    \"aspect.integrateddynamics.read.double.energy.fillratio\": \"Energy Fill Ratio\",\n    \"aspect.integrateddynamics.read.double.energy.fillratio.info\": \"The amount of energy in the target divided by its capacity\",\n\n    \"_comment\": \"# Network aspects\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"Is Network\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"If the target has a network\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"Elements\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"The amount of elements in the target network\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"Energy Batteries\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"The amount of energy batteries in the target network\",\n    \"aspect.integrateddynamics.read.long.network.energy.stored\": \"Energy Stored\",\n    \"aspect.integrateddynamics.read.long.network.energy.stored.info\": \"The amount of energy available in the target network\",\n    \"aspect.integrateddynamics.read.long.network.energy.max\": \"Energy Capacity\",\n    \"aspect.integrateddynamics.read.long.network.energy.max.info\": \"The energy capacity of the network\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"Energy Consumption Rate\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"The current energy consumption rate of all ID parts within the target network per tick\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"Value\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"The value that is being exposed by the target\",\n    \"aspect.integrateddynamics.read.operator.network.variablebyid\": \"Variable Value By ID\",\n    \"aspect.integrateddynamics.read.operator.network.variablebyid.info\": \"An operator that returns the value of a variable by its integer identifier\",\n\n    \"_comment\": \"# Redstone aspects\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"Redstone High\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"If the redstone level is at its maximum\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"Redstone Low\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"If there is no redstone level\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"Redstone\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"If there is a redstone level\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"Redstone Clock\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"Pulses with a configurable length and interval.\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"Redstone\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"Get the exact redstone level\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"Comparator\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"Get the comparator value\",\n\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"Redstone\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"Outputs a full redstone signal if true\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"Redstone Pulse\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"Outputs a pulse with the configured redstone value if the given value changes.\",\n\n    \"aspect.integrateddynamics.write.integer.redstone\": \"Redstone\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"Outputs the value as exact redstone signal\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"Redstone Pulse\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"Outputs a pulse with the configured redstone value if the given value changes.\",\n\n    \"_comment\": \"# World aspects\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"Has Entity\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"If the target space has an entity\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"Has Mob\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"If the target space has a mob\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"Has Animal\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"If the target space has an animal\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"Has Player\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"If the target space has a player\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"Has Item\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"If the target space has an item\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"Weather: Clear\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"If there is a clear weather in the world\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"Weather: Rain\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining.info\": \"If it is raining in the world\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"Weather: Thunder\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"If there is a thunderstorm in the world\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"Is Day\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"If it is day in the target world\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"Is Night\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"If it is night in the target world\",\n\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"Entities\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"The amount of entities at the target space\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"Rain Countdown\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"The amount of ticks until it will rain in this world\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"Tick time\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"The average tick time in milliseconds for this world\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"Day Time\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"The time that has passed in the target world day\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"Light Level\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"The light level at the target\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"Player Count\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"The players that are online in this world\",\n\n    \"aspect.integrateddynamics.read.double.world.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"The number of ticks per second in this world\",\n\n    \"aspect.integrateddynamics.read.long.world.time\": \"Time\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"The world time dependent of the set time command\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"Total Time\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"The world time independent of the set time command\",\n\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"World Name\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"The target world name\",\n\n    \"aspect.integrateddynamics.read.list.world.players\": \"Players\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"The players that are in this world\",\n\n    \"_comment\": \"Aspect property types\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"Tank ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"Slot ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"List Element\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"Range\",\n\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"Channel\",\n\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"Pulse Interval\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"Pulse Length\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"Pulse Time Offset\",\n\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"Volume\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"Frequency\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"Range\",\n\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"X Offset\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Y Offset\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Z Offset\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"Particle Count\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"X Spread\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Y Spread\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Z Spread\",\n\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"Strong Power\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"If a Redstone Torch should be simulated instead of regular Redstone Dust\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"Pulse Value\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"The pulse value that should be emitted.\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_length\": \"Pulse Length\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_length.info\": \"The duration of the pulse in ticks.\",\n\n    \"_comment\": \"Operators\",\n    \"operator.integrateddynamics\": \"Operator\",\n    \"operator.integrateddynamics.applied.operator_name\": \"Applied %s [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"§Applied to: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§eOperator: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§eCategory: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§eVariable IDs: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§eInput Type %s: §r\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§eOutput Type: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"The operator %s received an input of length %s while it needs a length of %s.\",\n    \"operator.integrateddynamics.error.operator_param_wrong_input_length\": \"An operator input length of %s is required, while %s only has an input length of %s.\",\n    \"operator.integrateddynamics.error.null_type\": \"The operator %s received an input with a null variable at position %s.\",\n    \"operator.integrateddynamics.error.wrong_type\": \"The operator %s received an input with type %s at position %s while the type %s was expected.\",\n    \"operator.integrateddynamics.error.wrong_type_output\": \"The evaluation for operator %s returned %s instead of the expected %s.\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"The operator %s received an input with type %s at position %s while the type %s was expected for currying.\",\n    \"operator.integrateddynamics.error.currying_overflow\": \"While currying the operator %s (%s inputs) with %s inputs, an operator was expected as output, but got %s.\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"The operator %s received an input operator that outputs the type %s while the type %s was expected.\",\n    \"operator.integrateddynamics.error.illegal_property\": \"The property is expected to output %s, while %s was found for operator %s.\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"Variable with id %s could not be found within the current network.\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"Variable with id %s depends on itself, infinite recursion.\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"The operator %s received the operator %s with input length %s while length %s is required.\",\n    \"operator.integrateddynamics.error.operator_recursion_limit\": \"Reached the operator recursion limit of %s invocations for %s. Trying to crash the game, are you?\",\n    \"operator.integrateddynamics.error.operator_nbt_path_expression\": \"Found an invalid NBT path expression '%s': %s\",\n    \"operator.integrateddynamics.error.cast.no_mapping\": \"No cast mapping exists from %s to %s.\",\n    \"operator.integrateddynamics.error.cast.illegal\": \"Attempted to cast %s to %s, for value \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.cast.unexpected\": \"The value of type %s does not correspond to the expected type %s to cast to %s.\",\n    \"operator.integrateddynamics.error.divide_by_zero\": \"Division by zero is not allowed.\",\n    \"operator.integrateddynamics.error.parse\": \"Value \\\"%s\\\" could not be parsed to a %s.\",\n    \"operator.integrateddynamics.error.no_derserializer\": \"No serializer was found to deserialize the operator value \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.regex.invalid\": \"Invalid regex: \\\"%s\\\"\",\n    \"operator.integrateddynamics.error.substring.to_greater_than_from\": \"The substring end position must be strictly larger than the start position.\",\n    \"operator.integrateddynamics.error.substring.index_negative\": \"Substring indexes must not be negative.\",\n    \"operator.integrateddynamics.error.substring.longer_than_string\": \"Substring indexes must not exceed the string length.\",\n    \"operator.integrateddynamics.error.group.index_negative\": \"The group index must not be negative.\",\n    \"operator.integrateddynamics.error.group.no_match\": \"No group regex match was found for regex \\\"%s\\\" in \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.group.no_match_group\": \"No group regex match was found for regex \\\"%s\\\" in \\\"%s\\\" for group \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.regex_scan.index_negative\": \"The regex scan index must not be negative.\",\n    \"operator.integrateddynamics.error.regex_scan.no_match_group\": \"No regex scan match was found for regex \\\"%s\\\" in \\\"%s\\\" for group \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.replace_regex.invalid_group\": \"Invalid group in regex replacement for replacement \\\"%s\\\": %s\",\n    \"operator.integrateddynamics.error.infinite_list.illegal\": \"The operator %s can not be applied on infinite lists.\",\n    \"operator.integrateddynamics.error.index_out_of_bounds\": \"Index out of bounds. Tried to get element %s of a list of length %s. Use the getOrDefault operator to provide a default when an index is out of bounds.\",\n    \"operator.integrateddynamics.error.slice.to_greater_than_from\": \"The slice end position must be strictly larger than the start position.\",\n    \"operator.integrateddynamics.error.slice.index_negative\": \"Slice indexes must not be negative.\",\n    \"operator.integrateddynamics.error.reduce.empty\": \"The reduce1 operator tried to get the head of an empty list. Use the reduce operator instead to provide a base value to support empty lists.\",\n    \"operator.integrateddynamics.error.operator_not_found\": \"Could not find the operator with name \\\"%s\\\".\",\n\n    \"operator.integrateddynamics.logical\": \"Logical\",\n    \"operator.integrateddynamics.logical.basename\": \"Logical %s\",\n    \"operator.integrateddynamics.logical.and\": \"And\",\n    \"operator.integrateddynamics.logical.or\": \"Or\",\n    \"operator.integrateddynamics.logical.not\": \"Not\",\n    \"operator.integrateddynamics.logical.nand\": \"Nand\",\n    \"operator.integrateddynamics.logical.nor\": \"Nor\",\n\n    \"operator.integrateddynamics.arithmetic\": \"Arithmetic\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"Arithmetic %s\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"Addition\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"Subtraction\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"Multiplication\",\n    \"operator.integrateddynamics.arithmetic.division\": \"Division\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"Maximum\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"Takes the largest of two values.\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"Minimum\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"Takes the smallest of two values.\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"Modulus\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"Increment\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"Decrement\",\n\n    \"operator.integrateddynamics.integer\": \"Integer\",\n    \"operator.integrateddynamics.integer.basename\": \"Integer %s\",\n    \"operator.integrateddynamics.integer.modulus\": \"Modulus\",\n    \"operator.integrateddynamics.integer.increment\": \"Increment\",\n    \"operator.integrateddynamics.integer.decrement\": \"Decrement\",\n\n    \"operator.integrateddynamics.relational\": \"Relational\",\n    \"operator.integrateddynamics.relational.basename\": \"Relational %s\",\n    \"operator.integrateddynamics.relational.equals\": \"Equals\",\n    \"operator.integrateddynamics.relational.equals.info\": \"Tests whether two values are equal. The types of both must be the same.\",\n    \"operator.integrateddynamics.relational.gt\": \"Greater Than\",\n    \"operator.integrateddynamics.relational.lt\": \"Less Than\",\n    \"operator.integrateddynamics.relational.notequals\": \"Not Equals\",\n    \"operator.integrateddynamics.relational.notequals.info\": \"Tests whether two values are unequal. The types of both must be the same.\",\n    \"operator.integrateddynamics.relational.ge\": \"Greater Than or Equal\",\n    \"operator.integrateddynamics.relational.le\": \"Less Than or Equal\",\n\n    \"operator.integrateddynamics.binary\": \"Binary\",\n    \"operator.integrateddynamics.binary.basename\": \"Binary %s\",\n    \"operator.integrateddynamics.binary.and\": \"And\",\n    \"operator.integrateddynamics.binary.or\": \"Or\",\n    \"operator.integrateddynamics.binary.xor\": \"Xor\",\n    \"operator.integrateddynamics.binary.complement\": \"Complement\",\n    \"operator.integrateddynamics.binary.rshift\": \"Right Shift\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"Right Shift with ones fill\",\n    \"operator.integrateddynamics.binary.lshift\": \"Left Shift\",\n    \"operator.integrateddynamics.binary.rzshift\": \"Right Zero Shift\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"Right Shift with zeros fill\",\n\n    \"operator.integrateddynamics.string\": \"String\",\n    \"operator.integrateddynamics.string.basename\": \"String %s\",\n    \"operator.integrateddynamics.string.length\": \"Length\",\n    \"operator.integrateddynamics.string.length.info\": \"The length of the given String\",\n    \"operator.integrateddynamics.string.concat\": \"Concat\",\n    \"operator.integrateddynamics.string.concat.info\": \"Concatenate the two given Strings to one\",\n    \"operator.integrateddynamics.string.contains\": \"String Contains\",\n    \"operator.integrateddynamics.string.contains.info\": \"If the given substring is contained within the given string.\",\n    \"operator.integrateddynamics.string.contains_regex\": \"Contains Regex\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"If the regular expression given matches any substring of the given string.\",\n    \"operator.integrateddynamics.string.matches_regex\": \"Matches Regex\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"If the regular expression given matches the given string.\",\n    \"operator.integrateddynamics.string.index_of\": \"Index Of\",\n    \"operator.integrateddynamics.string.index_of.info\": \"Get the index of the first occurrence of a substring matching the search term in the given string.\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"Index Of Regex\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"Get the index of the first occurrence of a substring matching the pattern in the given string.\",\n    \"operator.integrateddynamics.string.starts_with\": \"Starts With\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"If the given substring matches the start of the given string.\",\n    \"operator.integrateddynamics.string.ends_with\": \"Ends With\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"If the string given matches the end of the given string.\",\n    \"operator.integrateddynamics.string.split_on\": \"Split On\",\n    \"operator.integrateddynamics.string.split_on.info\": \"Get a list containing pieces, split on the given (literal) delimiter, of the given string.\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"Split On Regex\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"Get a list containing pieces, split on the given regular expression, of the given string.\",\n    \"operator.integrateddynamics.string.substring\": \"Substring\",\n    \"operator.integrateddynamics.string.substring.info\": \"Get a substring, from the given index (inclusive) to the given index (exclusive), of the given string.\",\n    \"operator.integrateddynamics.string.regex_group\": \"Regex Group\",\n    \"operator.integrateddynamics.string.regex_group.info\": \"Search for the given regular expression and return in that match the group at the given index, in the given string.\",\n    \"operator.integrateddynamics.string.regex_groups\": \"Regex Groups\",\n    \"operator.integrateddynamics.string.regex_groups.info\": \"Search for the given regular expression and return in that match a list of all its groups, in the given string.\",\n    \"operator.integrateddynamics.string.regex_scan\": \"Regex Scan\",\n    \"operator.integrateddynamics.string.regex_scan.info\": \"Search for all matches of the given regular expression and return the group at the given index, in the given string.\",\n    \"operator.integrateddynamics.string.replace\": \"Replace\",\n    \"operator.integrateddynamics.string.replace.info\": \"Find all instances of the search term and replace them with the given string, in the given string.\",\n    \"operator.integrateddynamics.string.replace_regex\": \"Replace Regex\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"Find all matches of the search pattern and replace them with the given string, in the given string.\",\n    \"operator.integrateddynamics.string.join\": \"Join\",\n    \"operator.integrateddynamics.string.join.info\": \"Create a joined string with the given string delimiter and the given list of strings.\",\n    \"operator.integrateddynamics.string.name\": \"To String\",\n    \"operator.integrateddynamics.string.name.info\": \"Get the string or name of a named object or converts it to a string\",\n    \"operator.integrateddynamics.string.unique_name\": \"Unique Name\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"Get the unique name of an object\",\n\n    \"operator.integrateddynamics.double\": \"Double\",\n    \"operator.integrateddynamics.double.basename\": \"Double %s\",\n    \"operator.integrateddynamics.double.sqrt\": \"Square Root\",\n    \"operator.integrateddynamics.double.pow\": \"Power\",\n\n    \"operator.integrateddynamics.number\": \"Number\",\n    \"operator.integrateddynamics.number.basename\": \"Number %s\",\n    \"operator.integrateddynamics.number.round\": \"Round\",\n    \"operator.integrateddynamics.number.round.info\": \"Round to the nearest Integer\",\n    \"operator.integrateddynamics.number.ceil\": \"Ceil\",\n    \"operator.integrateddynamics.number.ceil.info\": \"Round up to the nearest Integer\",\n    \"operator.integrateddynamics.number.floor\": \"Floor\",\n    \"operator.integrateddynamics.number.floor.info\": \"Round down to the nearest Integer\",\n    \"operator.integrateddynamics.number.compact\": \"Compact\",\n    \"operator.integrateddynamics.number.compact.info\": \"Show a shorter, less precise representation of a Number\",\n    \"operator.integrateddynamics.number.cast_double\": \"Cast Number to Double\",\n    \"operator.integrateddynamics.number.cast_double.info\": \"Cast any Number to a Double\",\n    \"operator.integrateddynamics.number.cast_long\": \"Cast Number to Long\",\n    \"operator.integrateddynamics.number.cast_long.info\": \"Cast any Number to a Long\",\n    \"operator.integrateddynamics.number.cast_integer\": \"Cast Number to Integer\",\n    \"operator.integrateddynamics.number.cast_integer.info\": \"Cast any Number to an Integer\",\n\n    \"operator.integrateddynamics.list\": \"List\",\n    \"operator.integrateddynamics.list.basename\": \"List %s\",\n    \"operator.integrateddynamics.list.length\": \"Length\",\n    \"operator.integrateddynamics.list.length.info\": \"The length of the given list\",\n    \"operator.integrateddynamics.list.empty\": \"Empty\",\n    \"operator.integrateddynamics.list.empty.info\": \"If the list is empty\",\n    \"operator.integrateddynamics.list.notempty\": \"Not Empty\",\n    \"operator.integrateddynamics.list.notempty.info\": \"If the list is not empty\",\n    \"operator.integrateddynamics.list.get\": \"Get\",\n    \"operator.integrateddynamics.list.get.info\": \"Get the list element at the given position, throws an error if the index is out of bounds.\",\n    \"operator.integrateddynamics.list.get_or_default\": \"Get Or Default\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"Safely get the list element at the given position, if that element is not available, return the given default value.\",\n    \"operator.integrateddynamics.list.contains\": \"Contains\",\n    \"operator.integrateddynamics.list.contains.info\": \"If the list contains the given element.\",\n    \"operator.integrateddynamics.list.contains_p\": \"Contains Predicate\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"If at least one of the elements of the list returns true for the given predicate.\",\n    \"operator.integrateddynamics.list.count\": \"Count\",\n    \"operator.integrateddynamics.list.count.info\": \"The number of times the given element is found in the list.\",\n    \"operator.integrateddynamics.list.count_p\": \"Count Predicate\",\n    \"operator.integrateddynamics.list.count_p.info\": \"The number of times the given predicate returns true for the elements in the list.\",\n    \"operator.integrateddynamics.list.append\": \"Append\",\n    \"operator.integrateddynamics.list.append.info\": \"Append to the given list the given item.\",\n    \"operator.integrateddynamics.list.concat\": \"Concat\",\n    \"operator.integrateddynamics.list.concat.info\": \"Concatenate the two lists to each other.\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"Lazy List Builder\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"Build a list lazily using a start value and an operator that is applied to the previous element to get a next element.\",\n    \"operator.integrateddynamics.list.head\": \"Head\",\n    \"operator.integrateddynamics.list.head.info\": \"Get the first element of the given list.\",\n    \"operator.integrateddynamics.list.tail\": \"Tail\",\n    \"operator.integrateddynamics.list.tail.info\": \"Remove the first element from the list.\",\n    \"operator.integrateddynamics.list.uniq_p\": \"Unique Predicate\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"Deduplicate all elements in the list based on the given comparator predicate.\",\n    \"operator.integrateddynamics.list.uniq\": \"Uniq\",\n    \"operator.integrateddynamics.list.uniq.info\": \"Deduplicate all elements in the list.\",\n    \"operator.integrateddynamics.list.slice\": \"Slice\",\n    \"operator.integrateddynamics.list.slice.info\": \"Take a subset of the given list from the given index (inclusive) to the given index (exclusive).\",\n    \"operator.integrateddynamics.list.intersection\": \"Intersection\",\n    \"operator.integrateddynamics.list.intersection.info\": \"Returns a list of items present in both supplied lists\",\n    \"operator.integrateddynamics.list.equals_set\": \"List Equals (Set)\",\n    \"operator.integrateddynamics.list.equals_set.info\": \"Check if two lists are equal according to set semantics.\",\n    \"operator.integrateddynamics.list.equals_multiset\": \"List Equals (Multiset)\",\n    \"operator.integrateddynamics.list.equals_multiset.info\": \"Check if two lists are equal according to multiset semantics.\",\n\n    \"operator.integrateddynamics.block\": \"Block\",\n    \"operator.integrateddynamics.block.basename\": \"Block %s\",\n    \"operator.integrateddynamics.block.opaque\": \"Opaque\",\n    \"operator.integrateddynamics.block.opaque.info\": \"If the given block is opaque, non-transparent\",\n    \"operator.integrateddynamics.block.itemstack\": \"Item\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"The item representation of the given block\",\n    \"operator.integrateddynamics.block.mod\": \"Mod\",\n    \"operator.integrateddynamics.block.mod.info\": \"The name of the mod owning the given block\",\n    \"operator.integrateddynamics.block.breaksound\": \"Block Break Sound\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"The break sound of the given block\",\n    \"operator.integrateddynamics.block.placesound\": \"Block Place Sound\",\n    \"operator.integrateddynamics.block.placesound.info\": \"The place sound of the given block\",\n    \"operator.integrateddynamics.block.stepsound\": \"Block Step Sound\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"The step sound of the given block\",\n    \"operator.integrateddynamics.block.isshearable\": \"Block Is Shearable\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"If the given block is shearable\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"Block Can Sustain Block Plant\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"If the given block can sustain the given block plant\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"Block Can Sustain Item Plant\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"If the given block can sustain the given item plant\",\n    \"operator.integrateddynamics.block.plantage\": \"Block Plant Age\",\n    \"operator.integrateddynamics.block.plantage.info\": \"The age of the given plant block\",\n    \"operator.integrateddynamics.block.blockbyname\": \"Block By Name\",\n    \"operator.integrateddynamics.block.blockbyname.info\": \"Get the block corresponding to the given unique name.\",\n    \"operator.integrateddynamics.block.blockproperties\": \"Block Properties\",\n    \"operator.integrateddynamics.block.blockproperties.info\": \"Get the block properties as NBT compound tag.\",\n    \"operator.integrateddynamics.block.blockfromproperties\": \"Block Properties\",\n    \"operator.integrateddynamics.block.blockfromproperties.info\": \"Get the given block applied with the given properties.\",\n    \"operator.integrateddynamics.block.blockpossibleproperties\": \"Block Properties\",\n    \"operator.integrateddynamics.block.blockpossibleproperties.info\": \"Get all possible block properties as an NBT compound tag with list values.\",\n    \"operator.integrateddynamics.block.tag\": \"Block Tag Names\",\n    \"operator.integrateddynamics.block.tag.info\": \"The Tag names (strings) of the given block\",\n    \"operator.integrateddynamics.string.blocktag\": \"Block Tag Values\",\n    \"operator.integrateddynamics.string.blocktag.info\": \"The Tag values (blocks) of the given block tag name\",\n\n    \"operator.integrateddynamics.itemstack\": \"Item\",\n    \"operator.integrateddynamics.itemstack.basename\": \"Item %s\",\n    \"operator.integrateddynamics.itemstack.size\": \"Size\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"The current stack size\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"Max size\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"The maximum stack size\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"Stackable\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"If the item stack can hold at least two items.\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"Damageable\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"If the item can take damage.\",\n    \"operator.integrateddynamics.itemstack.damage\": \"Damage\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"The current item damage\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"Max Damage\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"The maximum item damage\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"Enchanted\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"If the item is enchanted\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"Enchantable\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"If the item can be enchanted\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"Repair Cost\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"The repair cost of the item\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"Rarity\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"The rarity of the item\",\n    \"operator.integrateddynamics.itemstack.strength\": \"Strength vs Block\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"The strength this item has against the given block\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"Can Harvest\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"If the item can harvest the given block\",\n    \"operator.integrateddynamics.itemstack.block\": \"Block\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"The block from the given item\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"Has Fluid\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"If the given item has a fluid\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"Fluid\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"The fluid from the given item\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"Fluid Capacity\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"The fluid capacity of the given item in mB\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"NBT equals\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"If the data components of the given items are equal\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt\": \"Raw item equals no NBT\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt.info\": \"If the raw items of the given stacks are equal, ignoring data components and item count but including damage values.\",\n    \"operator.integrateddynamics.itemstack.israwitemequal\": \"Raw item equals\",\n    \"operator.integrateddynamics.itemstack.israwitemequal.info\": \"If the raw items of the given items are equal, ignoring NBT, item count, and damage values.\",\n    \"operator.integrateddynamics.itemstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"The name of the mod owning the given item\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"Fuel Burn Time\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"The fuel burn time in ticks of the given item\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"Is Fuel\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"If the given item can be used as fuel\",\n    \"operator.integrateddynamics.itemstack.tag\": \"Item Tag Names\",\n    \"operator.integrateddynamics.itemstack.tag.info\": \"The Tag names (strings) of the given item\",\n    \"operator.integrateddynamics.string.tag\": \"Item Tag Values\",\n    \"operator.integrateddynamics.string.tag.info\": \"The Tag values (items) of the given item tag name\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"Item Has Inventory\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"If the given item has an inventory\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"Item Inventory Size\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"Retrieve the inventory size of the given item handler contents\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"Item Inventory\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"Retrieve the inventory of the given item handler contents\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"Item With Stacksize\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"Set the stacksize for the given item\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"Item By Name\",\n    \"operator.integrateddynamics.itemstack.itembyname.info\": \"Get the item corresponding to the given unique name.\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"Item List Count\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"Get the total item count of exactly the given item in a list.\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"Item NBT\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"Get the data of the given item as NBT.\",\n    \"operator.integrateddynamics.itemstack.hasnbt\": \"Has NBT\",\n    \"operator.integrateddynamics.itemstack.hasnbt.info\": \"If the item stack has data components.\",\n    \"operator.integrateddynamics.itemstack.datakeys\": \"Data Keys\",\n    \"operator.integrateddynamics.itemstack.datakeys.info\": \"Get all data component keys of the item stack.\",\n    \"operator.integrateddynamics.itemstack.datavalue\": \"Data Value\",\n    \"operator.integrateddynamics.itemstack.datavalue.info\": \"Get the value of the given data component key.\",\n    \"operator.integrateddynamics.itemstack.withdata\": \"With Data\",\n    \"operator.integrateddynamics.itemstack.withdata.info\": \"Add the given data component key and value to the given item stack.\",\n\n    \"operator.integrateddynamics.entity\": \"Entity\",\n    \"operator.integrateddynamics.entity.basename\": \"Entity %s\",\n    \"operator.integrateddynamics.entity.ismob\": \"Is Mob\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"If the entity is a mob\",\n    \"operator.integrateddynamics.entity.isanimal\": \"Is Animal\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"If the entity is an animal\",\n    \"operator.integrateddynamics.entity.isitem\": \"Is Item\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"If the entity is an item\",\n    \"operator.integrateddynamics.entity.isplayer\": \"Is Player\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"If the entity is a player\",\n    \"operator.integrateddynamics.entity.isminecart\": \"Is Minecart\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"If the entity is a minecart\",\n    \"operator.integrateddynamics.entity.item\": \"Item\",\n    \"operator.integrateddynamics.entity.item.info\": \"The item from the item entity\",\n    \"operator.integrateddynamics.entity.health\": \"Health\",\n    \"operator.integrateddynamics.entity.health.info\": \"The entity health\",\n    \"operator.integrateddynamics.entity.width\": \"Width\",\n    \"operator.integrateddynamics.entity.width.info\": \"The entity width\",\n    \"operator.integrateddynamics.entity.height\": \"Height\",\n    \"operator.integrateddynamics.entity.height.info\": \"The entity height\",\n    \"operator.integrateddynamics.entity.isburning\": \"Is Burning\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"If the entity is burning\",\n    \"operator.integrateddynamics.entity.iswet\": \"Is Wet\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"If the entity is wet\",\n    \"operator.integrateddynamics.entity.iscrouching\": \"Is Crouching\",\n    \"operator.integrateddynamics.entity.iscrouching.info\": \"If the entity is crouching\",\n    \"operator.integrateddynamics.entity.iseating\": \"Is Eating\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"If the entity is eating\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"Armor Inventory\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"The list of items the entity is carrying as armor.\",\n    \"operator.integrateddynamics.entity.inventory\": \"Player Inventory\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"The list of items the player is carrying.\",\n    \"operator.integrateddynamics.entity.mod\": \"Mod\",\n    \"operator.integrateddynamics.entity.mod.info\": \"The name of the mod owning the given entity\",\n    \"operator.integrateddynamics.entity.targetblock\": \"Target Block\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"The block the given entity is currently looking at.\",\n    \"operator.integrateddynamics.entity.targetentity\": \"Target Entity\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"The entity the given entity is currently looking at.\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"Has Gui Open\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"If the given player has an external gui open.\",\n    \"operator.integrateddynamics.entity.helditem\": \"Held Item Mainhand\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"The item the given entity is currently holding in its main hand.\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"Held Item Offhand\",\n    \"operator.integrateddynamics.entity.helditemoffhand.info\": \"The item the given entity is currently holding in its off-hand.\",\n    \"operator.integrateddynamics.entity.mounted\": \"Mounted Entities\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"The entities that are currently mounted on the given entity.\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"Item Frame Contents\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"The contents from the given Item Frame.\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"Item Frame Rotation\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"The rotation from the given Item Frame.\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"Entity Hurt Sound\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"The hurt sound of the given entity.\",\n    \"operator.integrateddynamics.entity.deathsound\": \"Entity Death Sound\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"The death sound of the given entity.\",\n    \"operator.integrateddynamics.entity.age\": \"Entity Age\",\n    \"operator.integrateddynamics.entity.age.info\": \"The age of the given entity.\",\n    \"operator.integrateddynamics.entity.ischild\": \"Entity Is Child\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"If the given entity is a child.\",\n    \"operator.integrateddynamics.entity.canbreed\": \"Entity Can Breed\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"If the given entity is ready to be bred.\",\n    \"operator.integrateddynamics.entity.isinlove\": \"Entity Is In Love\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"If the given entity is in love and is ready to breed.\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"Entity Can Breed With\",\n    \"operator.integrateddynamics.entity.canbreedwith.info\": \"If the given entity can be bred using the given item.\",\n    \"operator.integrateddynamics.entity.isshearable\": \"Entity Is Shearable\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"If the given entity is shearable\",\n    \"operator.integrateddynamics.entity.nbt\": \"Entity NBT\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"Get the given entity as NBT.\",\n    \"operator.integrateddynamics.entity.entitytype\": \"Entity Type\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"The entity type name.\",\n    \"operator.integrateddynamics.entity.entityitems\": \"Entity Items\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"The items that are contained within this entity.\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"Entity Fluids\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"The fluids that are contained within this entity.\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"Entity Energy Stored\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"The amount of energy that is stored in this entity.\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"Entity Energy Capacity\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"The energy capacity of this entity.\",\n\n    \"operator.integrateddynamics.entity.entityitemtooltip\": \"Entity Tooltip\",\n    \"operator.integrateddynamics.entity.entityitemtooltip.info\": \"In the context of the given Player Entity, get the tooltip of the given item as a list of strings.\",\n    \"operator.integrateddynamics.itemstack.tooltip\": \"Tooltip\",\n    \"operator.integrateddynamics.itemstack.tooltip.info\": \"Get the tooltip of the given item as a list of strings.\",\n\n    \"operator.integrateddynamics.itemstack.isenergycontainer\": \"Is Energy Container\",\n    \"operator.integrateddynamics.itemstack.isenergycontainer.info\": \"If the given item can hold energy\",\n    \"operator.integrateddynamics.itemstack.storedenergy\": \"Energy Stored\",\n    \"operator.integrateddynamics.itemstack.storedenergy.info\": \"The amount of energy stored in this item\",\n    \"operator.integrateddynamics.itemstack.energycapacity\": \"Energy Capacity\",\n    \"operator.integrateddynamics.itemstack.energycapacity.info\": \"The maximum amount of energy that can be stored in this item\",\n\n    \"operator.integrateddynamics.fluidstack\": \"Fluid\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"Fluid %s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"Amount\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"The amount of mB\",\n    \"operator.integrateddynamics.fluidstack.block\": \"Block\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"The block of the fluid\",\n    \"operator.integrateddynamics.fluidstack.light_level\": \"Light level\",\n    \"operator.integrateddynamics.fluidstack.light_level.info\": \"The light level emitted by the fluid\",\n    \"operator.integrateddynamics.fluidstack.density\": \"Density\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"The fluid density\",\n    \"operator.integrateddynamics.fluidstack.temperature\": \"Temperature\",\n    \"operator.integrateddynamics.fluidstack.temperature.info\": \"The fluid temperature\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"Viscosity\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"The fluid viscosity\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air\": \"Is Lighter Than Air\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air.info\": \"If the fluid is lighter than air\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"Rarity\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"The fluid rarity\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize\": \"Fluid vaporize sound\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize.info\": \"The fluid vaporize sound for the fluid\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty\": \"Bucket empty sound\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty.info\": \"The bucket empty sound for the fluid\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill\": \"Bucket fill sound\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill.info\": \"The bucket fill sound for the fluid\",\n    \"operator.integrateddynamics.fluidstack.bucket\": \"Bucket\",\n    \"operator.integrateddynamics.fluidstack.bucket.info\": \"The filled bucket for the fluid\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal\": \"Raw fluid equals\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal.info\": \"If the raw fluids are equal, ignoring quantity\",\n    \"operator.integrateddynamics.fluidstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"The name of the mod owning the given fluid\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"Fluid NBT\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"Get the data of the given fluid as NBT.\",\n    \"operator.integrateddynamics.fluidstack.with_amount\": \"Fluid With Amount\",\n    \"operator.integrateddynamics.fluidstack.with_amount.info\": \"Copy the given fluid with the given amount\",\n    \"operator.integrateddynamics.fluidstack.datakeys\": \"Data Keys\",\n    \"operator.integrateddynamics.fluidstack.datakeys.info\": \"Get all data component keys of the fluid stack.\",\n    \"operator.integrateddynamics.fluidstack.datavalue\": \"Data Value\",\n    \"operator.integrateddynamics.fluidstack.datavalue.info\": \"Get the value of the given data component key.\",\n    \"operator.integrateddynamics.fluidstack.withdata\": \"With Data\",\n    \"operator.integrateddynamics.fluidstack.withdata.info\": \"Add the given data component key and value to the given fluid stack.\",\n    \"operator.integrateddynamics.fluidstack.tag\": \"Fluid Tag Names\",\n    \"operator.integrateddynamics.fluidstack.tag.info\": \"The Tag names (strings) of the given fluid\",\n    \"operator.integrateddynamics.string.fluidtag\": \"Fluid Tag Values\",\n    \"operator.integrateddynamics.string.fluidtag.info\": \"The Tag values (fluids) of the given fluid tag name\",\n    \"operator.integrateddynamics.fluidstack.fluidbyname\": \"Fluid By Name\",\n    \"operator.integrateddynamics.fluidstack.fluidbyname.info\": \"Get the fluid corresponding to the given unique name.\",\n\n    \"operator.integrateddynamics.nbt\": \"NBT\",\n    \"operator.integrateddynamics.nbt.basename\": \"NBT %s\",\n    \"operator.integrateddynamics.nbt.compound_size\": \"NBT Compound Size\",\n    \"operator.integrateddynamics.nbt.compound_size.info\": \"The number of entries inside the given NBT compound tag\",\n    \"operator.integrateddynamics.nbt.compound_keys\": \"NBT Compound Keys\",\n    \"operator.integrateddynamics.nbt.compound_keys.info\": \"The list of keys inside the given NBT compound tag\",\n    \"operator.integrateddynamics.nbt.compound_haskey\": \"NBT Compound Has Key\",\n    \"operator.integrateddynamics.nbt.compound_haskey.info\": \"If the given NBT compound tag contains the given key\",\n    \"operator.integrateddynamics.nbt.compound_type\": \"NBT Compound Entry Type\",\n    \"operator.integrateddynamics.nbt.compound_type.info\": \"The value type in the given NBT compound tag corresponding to the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_tag\": \"NBT Compound Value\",\n    \"operator.integrateddynamics.nbt.compound_value_tag.info\": \"The value of any type in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean\": \"NBT Compound Value Boolean\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean.info\": \"The Boolean value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_integer\": \"NBT Compound Value Integer\",\n    \"operator.integrateddynamics.nbt.compound_value_integer.info\": \"The Integer value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_long\": \"NBT Compound Value Long\",\n    \"operator.integrateddynamics.nbt.compound_value_long.info\": \"The Long value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_double\": \"NBT Compound Value Double\",\n    \"operator.integrateddynamics.nbt.compound_value_double.info\": \"The Double value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_string\": \"NBT Compound Value String\",\n    \"operator.integrateddynamics.nbt.compound_value_string.info\": \"The String value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_compound\": \"NBT Compound Value Compound\",\n    \"operator.integrateddynamics.nbt.compound_value_compound.info\": \"The Compound value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag\": \"NBT Compound Value List NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag.info\": \"The NBT List value in the given NBT compound tag with the given key\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte\": \"NBT Compound Value Byte Array\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte.info\": \"The Byte Array in the given NBT compound tag with the given key as Integer List\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int\": \"NBT Compound Value Integer Array\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int.info\": \"The Integer Array in the given NBT compound tag with the given key as Integer List\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long\": \"NBT Compound Value Long Array\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long.info\": \"The Long Array in the given NBT compound tag with the given key as Long List\",\n    \"operator.integrateddynamics.nbt.compound_without\": \"NBT Compound Without\",\n    \"operator.integrateddynamics.nbt.compound_without.info\": \"Get a copy of the given NBT compound tag without the given key\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean\": \"NBT Compound With Boolean\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean.info\": \"Get a copy of the given NBT compound tag with the given Boolean entry\",\n    \"operator.integrateddynamics.nbt.compound_with_short\": \"NBT Compound With Short\",\n    \"operator.integrateddynamics.nbt.compound_with_short.info\": \"Get a copy of the given NBT compound tag with the given Integer as a short entry\",\n    \"operator.integrateddynamics.nbt.compound_with_integer\": \"NBT Compound With Integer\",\n    \"operator.integrateddynamics.nbt.compound_with_integer.info\": \"Get a copy of the given NBT compound tag with the given Integer entry\",\n    \"operator.integrateddynamics.nbt.compound_with_long\": \"NBT Compound With Long\",\n    \"operator.integrateddynamics.nbt.compound_with_long.info\": \"Get a copy of the given NBT compound tag with the given Long entry\",\n    \"operator.integrateddynamics.nbt.compound_with_double\": \"NBT Compound With Double\",\n    \"operator.integrateddynamics.nbt.compound_with_double.info\": \"Get a copy of the given NBT compound tag with the given Double entry\",\n    \"operator.integrateddynamics.nbt.compound_with_float\": \"NBT Compound With Float\",\n    \"operator.integrateddynamics.nbt.compound_with_float.info\": \"Get a copy of the given NBT compound tag with the given Double as a float entry\",\n    \"operator.integrateddynamics.nbt.compound_with_string\": \"NBT Compound With String\",\n    \"operator.integrateddynamics.nbt.compound_with_string.info\": \"Get a copy of the given NBT compound tag with the given String entry\",\n    \"operator.integrateddynamics.nbt.compound_with_tag\": \"NBT Compound With NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_tag.info\": \"Get a copy of the given NBT compound tag with the given NBT entry\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag\": \"NBT Compound With NBT List\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag.info\": \"Get a copy of the given NBT compound tag with the given NBT List entry\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte\": \"NBT Compound With Byte List\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte.info\": \"Get a copy of the given NBT compound tag with the given Integer List as an NBT Byte Array entry\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int\": \"NBT Compound With Int List\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int.info\": \"Get a copy of the given NBT compound tag with the given NBT Integer Array entry\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long\": \"NBT Compound With Long List\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long.info\": \"Get a copy of the given NBT compound tag with the given NBT Long Array entry\",\n    \"operator.integrateddynamics.nbt.compound_subset\": \"NBT Compound Subset\",\n    \"operator.integrateddynamics.nbt.compound_subset.info\": \"If the first NBT compound tag is a subset of, or equal to the second NBT compound tag. This will recursively check nested tags.\",\n    \"operator.integrateddynamics.nbt.compound_union\": \"NBT Compound Union\",\n    \"operator.integrateddynamics.nbt.compound_union.info\": \"The union of the given NBT compound tags. Nested tags will be joined recursively.\",\n    \"operator.integrateddynamics.nbt.compound_intersection\": \"NBT Compound Intersection\",\n    \"operator.integrateddynamics.nbt.compound_intersection.info\": \"The intersection of the given NBT compound tags. Nested tags will be intersected recursively.\",\n    \"operator.integrateddynamics.nbt.compound_minus\": \"NBT Compound Minus\",\n    \"operator.integrateddynamics.nbt.compound_minus.info\": \"The difference of the given NBT compound tags. Nested tags will be subtracted recursively.\",\n    \"operator.integrateddynamics.nbt.as_boolean\": \"NBT Boolean As Boolean\",\n    \"operator.integrateddynamics.nbt.as_boolean.info\": \"Get the Boolean value of the given NBT Byte tag\",\n    \"operator.integrateddynamics.nbt.as_byte\": \"NBT Byte As Integer\",\n    \"operator.integrateddynamics.nbt.as_byte.info\": \"Get the Integer value of the given NBT Byte tag\",\n    \"operator.integrateddynamics.nbt.as_short\": \"NBT Short as Integer\",\n    \"operator.integrateddynamics.nbt.as_short.info\": \"Get the Integer value of the given NBT Short tag\",\n    \"operator.integrateddynamics.nbt.as_int\": \"NBT Integer As Integer\",\n    \"operator.integrateddynamics.nbt.as_int.info\": \"Get the Integer value of the given NBT Integer tag\",\n    \"operator.integrateddynamics.nbt.as_long\": \"NBT Long As Long\",\n    \"operator.integrateddynamics.nbt.as_long.info\": \"Get the Long value of the given NBT Long tag\",\n    \"operator.integrateddynamics.nbt.as_double\": \"NBT Double As Double\",\n    \"operator.integrateddynamics.nbt.as_double.info\": \"Get the Double value of the given NBT Double tag\",\n    \"operator.integrateddynamics.nbt.as_float\": \"NBT Float As Double\",\n    \"operator.integrateddynamics.nbt.as_float.info\": \"Get the Double value of the given NBT Float tag\",\n    \"operator.integrateddynamics.nbt.as_string\": \"NBT String As String\",\n    \"operator.integrateddynamics.nbt.as_string.info\": \"Get the String value of the given NBT String tag\",\n    \"operator.integrateddynamics.nbt.as_tag_list\": \"NBT List As NBT List\",\n    \"operator.integrateddynamics.nbt.as_tag_list.info\": \"Get the NBT List value of the given NBT List tag\",\n    \"operator.integrateddynamics.nbt.as_byte_list\": \"NBT Byte Array As Byte List\",\n    \"operator.integrateddynamics.nbt.as_byte_list.info\": \"Get the Byte List value of the given NBT Byte Array tag\",\n    \"operator.integrateddynamics.nbt.as_int_list\": \"NBT Integer Array As Integer List\",\n    \"operator.integrateddynamics.nbt.as_int_list.info\": \"Get the Integer List value of the given NBT Integer Array tag\",\n    \"operator.integrateddynamics.nbt.as_long_list\": \"NBT Long Array As Long List\",\n    \"operator.integrateddynamics.nbt.as_long_list.info\": \"Get the Long List value of the given NBT Long Array tag\",\n    \"operator.integrateddynamics.nbt.from_boolean\": \"NBT Byte From Boolean\",\n    \"operator.integrateddynamics.nbt.from_boolean.info\": \"Create an NBT Byte tag from the given Boolean value\",\n    \"operator.integrateddynamics.nbt.from_short\": \"NBT Short From Integer\",\n    \"operator.integrateddynamics.nbt.from_short.info\": \"Create an NBT Short tag from the given Integer value\",\n    \"operator.integrateddynamics.nbt.from_byte\": \"NBT Byte From Integer\",\n    \"operator.integrateddynamics.nbt.from_byte.info\": \"Create an NBT Byte tag from the given Integer value\",\n    \"operator.integrateddynamics.nbt.from_int\": \"NBT Integer From Integer\",\n    \"operator.integrateddynamics.nbt.from_int.info\": \"Create an NBT Integer tag from the given Integer value\",\n    \"operator.integrateddynamics.nbt.from_long\": \"NBT Long From Long\",\n    \"operator.integrateddynamics.nbt.from_long.info\": \"Create an NBT Long tag from the given Long value\",\n    \"operator.integrateddynamics.nbt.from_double\": \"NBT Double From Double\",\n    \"operator.integrateddynamics.nbt.from_double.info\": \"Create an NBT Double tag from the given Double value\",\n    \"operator.integrateddynamics.nbt.from_float\": \"NBT Float From Double\",\n    \"operator.integrateddynamics.nbt.from_float.info\": \"Create an NBT Float tag from the given Double value\",\n    \"operator.integrateddynamics.nbt.from_string\": \"NBT String From String\",\n    \"operator.integrateddynamics.nbt.from_string.info\": \"Create an NBT String tag from the given String value\",\n    \"operator.integrateddynamics.nbt.from_tag_list\": \"NBT List From Tag List\",\n    \"operator.integrateddynamics.nbt.from_tag_list.info\": \"Create an NBT List tag from the given NBT List value\",\n    \"operator.integrateddynamics.nbt.from_byte_list\": \"NBT Byte Array From Byte List\",\n    \"operator.integrateddynamics.nbt.from_byte_list.info\": \"Create an NBT Byte Array tag from the given Integer List value\",\n    \"operator.integrateddynamics.nbt.from_int_list\": \"NBT Integer Array From Integer List\",\n    \"operator.integrateddynamics.nbt.from_int_list.info\": \"Create an NBT Integer Array tag from the given Integer List value\",\n    \"operator.integrateddynamics.nbt.from_long_list\": \"NBT Long Array From Long List\",\n    \"operator.integrateddynamics.nbt.from_long_list.info\": \"Create an NBT Long Array tag from the given Long List value\",\n    \"operator.integrateddynamics.nbt.path_match_first\": \"NBT Path Match First\",\n    \"operator.integrateddynamics.nbt.path_match_first.info\": \"Apply the given NBT Path expression on the given NBT value, and return the first match\",\n    \"operator.integrateddynamics.nbt.path_match_all\": \"NBT Path Match All\",\n    \"operator.integrateddynamics.nbt.path_match_all.info\": \"Apply the given NBT Path expression on the given NBT value, and return all matches as a list\",\n    \"operator.integrateddynamics.nbt.path_test\": \"NBT Path Test\",\n    \"operator.integrateddynamics.nbt.path_test.info\": \"Test if the given NBT Path expression matches with the given NBT value\",\n\n    \"operator.integrateddynamics.operator\": \"Operator\",\n    \"operator.integrateddynamics.operator.basename\": \"Operator %s\",\n    \"operator.integrateddynamics.operator.apply\": \"Apply\",\n    \"operator.integrateddynamics.operator.apply.info\": \"Apply for a given operator a given value.\",\n    \"operator.integrateddynamics.operator.apply2\": \"Apply 2\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"Apply for a given operator the given two values.\",\n    \"operator.integrateddynamics.operator.apply3\": \"Apply 3\",\n    \"operator.integrateddynamics.operator.apply3.info\": \"Apply for a given operator the given three values.\",\n    \"operator.integrateddynamics.operator.apply_n\": \"Apply N\",\n    \"operator.integrateddynamics.operator.apply_n.info\": \"Apply for a given operator the given list of values.\",\n    \"operator.integrateddynamics.operator.apply0\": \"Apply 0\",\n    \"operator.integrateddynamics.operator.apply0.info\": \"Apply for a given operator without arguments.\",\n    \"operator.integrateddynamics.operator.map\": \"Map\",\n    \"operator.integrateddynamics.operator.map.info\": \"Apply the given operator on all elements of a list, resulting in a new list of mapped values.\",\n    \"operator.integrateddynamics.operator.filter\": \"Filter\",\n    \"operator.integrateddynamics.operator.filter.info\": \"Filter a list of elements by matching them all with the given predicate.\",\n    \"operator.integrateddynamics.operator.conjunction\": \"Conjunction\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"Takes the conjunction of two predicates.\",\n    \"operator.integrateddynamics.operator.disjunction\": \"Disjunction\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"Takes the disjunction of two predicates.\",\n    \"operator.integrateddynamics.operator.negation\": \"Negation\",\n    \"operator.integrateddynamics.operator.negation.info\": \"Takes the negation of a predicate.\",\n    \"operator.integrateddynamics.operator.pipe\": \"Pipe\",\n    \"operator.integrateddynamics.operator.pipe.info\": \"Create a new operator that pipes the output from the first operator to the second operator.\",\n    \"operator.integrateddynamics.operator.pipe2\": \"Pipe 2\",\n    \"operator.integrateddynamics.operator.pipe2.info\": \"Create a new operator that gives its input to the first and second operators, and pipes the outputs from both of them to the third operator.\",\n    \"operator.integrateddynamics.operator.flip\": \"Flip\",\n    \"operator.integrateddynamics.operator.flip.info\": \"Flip the two first input parameters of an operator.\",\n    \"operator.integrateddynamics.virtual.flipped\": \"Flipped\",\n    \"operator.integrateddynamics.operator.reduce\": \"Reduce\",\n    \"operator.integrateddynamics.operator.reduce.info\": \"Apply the given operator on all elements of a list to reduce the list to one value.\",\n    \"operator.integrateddynamics.operator.reduce1\": \"Reduce 1\",\n    \"operator.integrateddynamics.operator.reduce1.info\": \"Apply the given operator on all elements of a list to reduce the list to one value. reduce1(op, list) is equivalent to reduce(op, tail(list), head(list)).\",\n    \"operator.integrateddynamics.operator.by_name\": \"Operator By Name\",\n    \"operator.integrateddynamics.operator.by_name.info\": \"Get the operator that has the given unique name.\",\n    \"operator.integrateddynamics.virtual.recipeoutputbyinput\": \"Recipe Output By Input\",\n    \"operator.integrateddynamics.virtual.recipeinputsbyoutput\": \"Recipe Inputs By Output\",\n    \"operator.integrateddynamics.virtual.recipesbyinput\": \"Recipes By Input\",\n    \"operator.integrateddynamics.virtual.recipesbyoutput\": \"Recipes By Output\",\n\n    \"operator.integrateddynamics.ingredients\": \"Ingredients\",\n    \"operator.integrateddynamics.ingredients.basename\": \"Ingredients %s\",\n    \"operator.integrateddynamics.ingredients.items\": \"Ingredient items\",\n    \"operator.integrateddynamics.ingredients.items.info\": \"The list of items\",\n    \"operator.integrateddynamics.ingredients.fluids\": \"Ingredient fluids\",\n    \"operator.integrateddynamics.ingredients.fluids.info\": \"The list of fluids\",\n    \"operator.integrateddynamics.ingredients.energies\": \"Ingredient energy elements\",\n    \"operator.integrateddynamics.ingredients.energies.info\": \"The list of energy elements\",\n    \"operator.integrateddynamics.ingredients.with_item\": \"Ingredients With Item\",\n    \"operator.integrateddynamics.ingredients.with_item.info\": \"Get a copy of the given ingredients with the given item at the given ingredient position\",\n    \"operator.integrateddynamics.ingredients.with_fluid\": \"Ingredients With Fluid\",\n    \"operator.integrateddynamics.ingredients.with_fluid.info\": \"Get a copy of the given ingredients with the given fluid at the given ingredient position\",\n    \"operator.integrateddynamics.ingredients.with_energy\": \"Ingredients With Energy\",\n    \"operator.integrateddynamics.ingredients.with_energy.info\": \"Get a copy of the given ingredients with the given energy at the given ingredient position\",\n    \"operator.integrateddynamics.ingredients.with_items\": \"Ingredients With Items\",\n    \"operator.integrateddynamics.ingredients.with_items.info\": \"Get a copy of the given ingredients with the given list of items at the given ingredient position\",\n    \"operator.integrateddynamics.ingredients.with_fluids\": \"Ingredients With Fluids\",\n    \"operator.integrateddynamics.ingredients.with_fluids.info\": \"Get a copy of the given ingredients with the given list of fluids at the given ingredient position\",\n    \"operator.integrateddynamics.ingredients.with_energies\": \"Ingredients With Energy Elements\",\n    \"operator.integrateddynamics.ingredients.with_energies.info\": \"Get a copy of the given ingredients with the given list of energy elements at the given ingredient position\",\n\n    \"operator.integrateddynamics.recipe\": \"Recipe\",\n    \"operator.integrateddynamics.recipe.basename\": \"Recipe %s\",\n    \"operator.integrateddynamics.recipe.input\": \"Recipe Input Ingredients\",\n    \"operator.integrateddynamics.recipe.input.info\": \"The input ingredients of the given recipe\",\n    \"operator.integrateddynamics.recipe.output\": \"Recipe Output Ingredients\",\n    \"operator.integrateddynamics.recipe.output.info\": \"The output ingredients of the given recipe\",\n    \"operator.integrateddynamics.recipe.with_input\": \"Recipe With Input Ingredients\",\n    \"operator.integrateddynamics.recipe.with_input.info\": \"Get a copy of the given recipe with the given ingredients as input\",\n    \"operator.integrateddynamics.recipe.with_output\": \"Recipe With Output Ingredients\",\n    \"operator.integrateddynamics.recipe.with_output.info\": \"Get a copy of the given recipe with the given ingredients as output\",\n    \"operator.integrateddynamics.recipe.with_input_output\": \"Recipe With Input and Output Ingredients\",\n    \"operator.integrateddynamics.recipe.with_input_output.info\": \"Create a recipe with the first ingredients as input, and the second ingredients as output\",\n\n    \"operator.integrateddynamics.general\": \"General\",\n    \"operator.integrateddynamics.general.basename\": \"General %s\",\n    \"operator.integrateddynamics.general.choice\": \"Choice\",\n    \"operator.integrateddynamics.general.choice.info\": \"If the first value is true, the second value is taken, otherwise the third value.\\\\nType two and three must be equal.\",\n    \"operator.integrateddynamics.general.identity\": \"Identity\",\n    \"operator.integrateddynamics.general.identity.info\": \"A copy of the input value.\",\n    \"operator.integrateddynamics.general.constant\": \"Constant\",\n    \"operator.integrateddynamics.general.constant.info\": \"A copy of the first input value.\",\n    \"operator.integrateddynamics.general.isnull\": \"Is Null\",\n    \"operator.integrateddynamics.general.isnull.info\": \"If the given value is null\",\n    \"operator.integrateddynamics.general.isnotnull\": \"Is Not Null\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"If the given value is not null\",\n\n    \"operator.integrateddynamics.virtual\": \"Virtual Operator\",\n    \"operator.integrateddynamics.virtual.basename\": \"Virtual %s\",\n    \"operator.integrateddynamics.virtual.p_conjunction\": \"Predicate Conjunction\",\n    \"operator.integrateddynamics.virtual.p_conjunction.info\": \"The conjunction of two predicates\",\n    \"operator.integrateddynamics.virtual.p_disjunction\": \"Predicate Disjunction\",\n    \"operator.integrateddynamics.virtual.p_disjunction.info\": \"The disjunction of two predicates\",\n    \"operator.integrateddynamics.virtual.p_negation\": \"Predicate Negation\",\n    \"operator.integrateddynamics.virtual.p_negation.info\": \"The disjunction of a predicate\",\n    \"operator.integrateddynamics.virtual.piped\": \"Piped Operator\",\n    \"operator.integrateddynamics.virtual.piped.info\": \"A chain of operators\",\n    \"operator.integrateddynamics.virtual.piped2\": \"Piped 2 Operator\",\n    \"operator.integrateddynamics.virtual.piped2.info\": \"A chain of piped 2 operators\",\n\n    \"operator.integrateddynamics.cast\": \"Cast\",\n    \"operator.integrateddynamics.cast.basename\": \"Cast\",\n    \"operator.integrateddynamics.cast.tooltip\": \"Cast %s to %s\",\n\n    \"operator.integrateddynamics.parse\": \"Parse\",\n    \"operator.integrateddynamics.parse.basename\": \"Parse\",\n    \"operator.integrateddynamics.parse.tooltip\": \"Parse %s\",\n\n    \"_comment\": \"------ On the Dynamics of Integration contents ------\",\n\n    \"info_book.integrateddynamics.section.main\": \"On the Dynamics of Integration\",\n    \"info_book.integrateddynamics.tag_index\": \"&oIndex&r\",\n\n    \"info_book.integrateddynamics.introduction\": \"Introduction\",\n    \"info_book.integrateddynamics.introduction.text1\": \"For too long, automation possibilities have been too limited. &1Integrated Dynamics&0 aims to be the go-to mod for automating your logic systems. It does this by providing an expressive immersive logic programming experience with hooks and support for most elements of &lMinecraft&r and its modifications.\",\n    \"info_book.integrateddynamics.introduction.text2\": \"Redstone circuits have always been a very basic, but workable and easily understandable method for automation. The basicness of redstone is however also an inconvenience. For automation networks with higher complexities, redstone circuits can become very large and consequently also difficult to manage. Furthermore, one small water-leak can cause major headaches to every redstoner.\",\n    \"info_book.integrateddynamics.introduction.text3\": \"The goal of this book is twofold. First, a &ltutorial&r is provided for learning the most important concepts behind this mod. Secondly, a detailed &lmanual&r explains all elements and mechanisms for players that are not not interested in the tutorial.\",\n    \"info_book.integrateddynamics.introduction.text4\": \"You can navigate through this book by clicking on the arrows at the bottom, shift clicking will flip through whole sections instead of single pages. At the start of large sections you will see a content table which will contain hyperlinks to its subsections for faster browsing. When you come across recipes, the items that are explained in this book are clickable as a fast reference to the section they are explained in. You will also notice a button at the top left of this book, which will take you to the section above your current one. The button at the top right corner will go back to the previously visited page each time when clicked.\",\n\n    \"info_book.integrateddynamics.tutorials\": \"Tutorials\",\n\n    \"info_book.integrateddynamics.tutorials.introduction\": \"Introduction\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"This part of the book contains several tutorials that will help you understand the basics of the mod. Furthermore, it will allow you to earn some &lrewards&r while accomplishing the achievements of each tutorial.\",\n\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"Menril Basics\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"To get started with this mod, you will need to find some &lMenril Trees&r which can most easily be found in &lMeneglin Biomes&r.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text2\": \"After that, you’ll need to craft a &lSqueezer&r and &lDrying Basin&r for processing &lMenril Logs&r. This can be done by placing a block of wood in the &lSqueezer&r and jumping on it until it's flat. Once that has been done, the &lMenril Resin&r will try to go to the two sides of the &lSqueezer&r. Next, place a &lDrying Basin&r next to the &lSqueezer&r to make it fill up with the &lMenril Resin&r you just squeezed.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"If you want to repeat the process, you can reset the &lSqueezer&r by applying a redstone pulse.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"In the future, you will also be able to craft upgraded variants of these machines that are more easily automatable, but are more costly: the &lMechanical Squeezer&r and the &lMechanical Drying Basin&r.\",\n\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"Network Foundations\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"The main goal of this mod is for building control networks for automation. This is done using &lLogic Cables&r. Try making a network with ten of those cables.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"The next step will be to craft some &lVariable Cards&r and &lVariable Transformers&r. &lVariable Cards&r are used to store references to values in the network. &lVariable Transformers&r are crafting elements for components that can be placed on &lLogic Cables&r for reading and writing these values.\",\n\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"Basic Network Components\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"One of the methods for binding &lVariable Cards&r to certain values, is by reading in values from &lReaders&r. Let’s start by crafting some basic readers. After that, craft a &lDisplay Panel&r, which is useful for displaying the values of &lVariable Cards&r.\",\n\n    \"info_book.integrateddynamics.tutorials.aspects\": \"Aspects\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"As a simple example, let’s try reading in a redstone value and displaying it.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text2\": \"Do this by attaching a &lRedstone Reader&r to a &lLogic Cable&r in the world. Make sure that the reader is targeting something with a redstone value, like a &lRedstone Torch&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"After that, right-click on the &lRedstone Reader&r to open the gui, after which you have to insert a &lVariable Card&r into the &6Integer&0 &lRedstone Value&r aspect, to bind the target’s redstone value to that &lVariable Card&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text4\": \"Next, place a &lDisplay Panel&r in the same network as the &lRedstone Reader&r, and insert your bound &lVariable Card&r to display its value.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"As a next step, craft a &lRedstone Writer&r and let it write a maximal integer redstone value that was read by a Redstone Reader.\",\n\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"Logic Operations\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"While displaying values that originate from readers can be useful, things get really interesting when you start combining certain values with other values using &2Operators&0. All &2Operators&0 can be found in the &lLogic Programmer&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"Start by crafting a &lLogic Programmer&r, which exists in both item and block-form. Open its gui to see the list of all &loperators&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"Next, search for the &6Integer&0 operator, and click on it. This will open a window in which you should type a number. When you have done this, insert an empty &lVariable Card&r to bind that &6Integer&0 to the &lVariable Card&r. When you’re done, do that once more for another &6Integer&0.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"Finally, search for the &lAddition&r operator and open it. This allows you to add two &eNumbers&0. Insert your two &6Integer&0 &lVariable Cards&r into these slots, and bind it to a new &lVariable Card&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text5\": \"After writing a value to a &lVariable Card&r, you can always modify it again later by inserting it again in the &lLogic Programmer&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text6\": \"As &1Integrated Dynamics&0 evaluates variables in a &olazy&r manner, this &lAddition&r &lVariable Card&r does not contain the addition of these &6Integers&0 directly, but the addition will only be performed when the value is needed in the network, based on the variable references. For this, the network has to be able to find these referenced &lVariable Cards&r. For this, you will need to craft and place a &lVariable Store&r. Insert the two &6Integer&0 &lVariable Cards&r into this store to make them available to the network.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text7\": \"Finally, you can now connect a cable to the &lVariable Store&r, attach a &lDisplay Panel&r, and insert the &lAddition&r &lVariable Card&r to display the addition.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text8\": \"The &lLogic Programmer&r and &lVariable Stores&r are going to be your best friends when you want to do more complex automation.\",\n\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"Recipe Handling\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text1\": \"&oRecipes&r are a big part of &lMinecraft&r. That is why this mod offers first-class support for them via &8Recipe&0 values, and &lIngredients&r values to represent their input and output collections. In this tutorial, we will have a look at how recipes can be obtained and used.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text2\": \"The easiest way to obtain &8Recipes&0 is simply by reading it from a machine that can handle recipes. So let's start by reading all crafting recipes by placing a &lMachine Reader&r against a &lCrafting Table&r and reading the list of recipes.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text3\": \"If you want some more fine-grained control over recipes, or if you are working with a machine that does not expose its recipes via the &lMachine Reader&r, you can visually create a &8Recipe&0 yourself in the &lLogic Programmer&r. Let's try creating a simple recipe, for example for crafting a &lChest&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text4\": \"In this last part of the tutorial, we'll take recipe reading one step further. Using the &lRecipe By Output&r aspect, we can retrieve an operator that, when applied to an &8Ingredients&0 instance, will return the first &8Recipe&0 that returns the given &8Ingredients&0 as output. We will use this to look up the recipe of a &lChest&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text5\": \"Let's start by creating an &8Ingredients&0 instance with a single &8Chest&0 in your &lLogic Programmer&r. Next, read the &lRecipe By Output&r aspect from a &lMachine Reader&r that targets a &lCrafting Table&r. After that, use the &oApply&r operator to apply your &lChest&r &8Ingredient&0 to the operator you got from the &lMachine Reader&r. Finally, insert the resulting variable into a &lDisplay Panel&r to see the &lChest&r's recipe. (Don't forget to insert all your used variables into a &lVariable Store&r).\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text6\": \"Obviously, there is much more you can do with &8Recipes&0 and &8Ingredients&0. Have a look in the &lMachine Reader&r and &lLogic Programmer&r to find out what other advanced functionality is available to you.\",\n\n    \"info_book.integrateddynamics.tutorials.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.tutorials.nbt.text1\": \"&4NBT&0 tags are used to represent complex datastructures. These are typically used internally in Minecraft to store machine and entity information. In this tutorial, we will go over simple NBT tag reading and manipulation\",\n    \"info_book.integrateddynamics.tutorials.nbt.text2\": \"As a first step, use a &lBlock Reader&r on a lectern, read its &4NBT&0 data, and visualize it in a &lDisplay Panel&r.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text3\": \"Next, from the read &lLectern&r &4NBT&0 tag, read the &3Book&0 sub-tag as a compound tag, and visualize it.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text4\": \"As a final step, we will read the &3id&0 field from this &3Book&0 sub-tag. However, instead of starting from our last read sub-tag, we will start from the root, and apply an &lNBT Path&r expression. Such expressions are useful to get deeply nested &4NBT&0 values. For example, applying the expression &4”.root.child1.child2”&0 on the &3NBT&0 tag &3{ root: { child1: { child2: ”some value” }}}&0 will output &3”some value”&0. More advanced features of &lNBT Path&r are described later in the advanced part of the manual.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text5\": \"To accomplish the final advancement, create an &lNBT Path&r expression that selects the &3Book&0/&3id&0 field, apply it on our &lLectern&r tag, and visualize it in a &lDisplay Panel&r.\",\n\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"Advanced Network Components\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"Now that you are familiar with basic logic, we can have a look at the more advanced network components: the &lMaterializer&r and the &lProxy&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"As you may remember, the &lLogic Programmer&r allows you to create variables with &ostatic&r values, or you can create more complex variables based on &odynamic&r value evaluation.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"The &lMaterializer&r can be used for converting a &odynamic&r value to a &ostatic&r one. This can be done by inserting the &odynamic&r &lVariable Card&r in the top slot of the &lMaterializer&r. Every &lVariable Card&r that is inserted into the left slot, will be set with the value the top variable has at that moment.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"The &lProxy&r allows you to add an extra level of indirection for your variables. You may for example have a logic program that requires a &ostatic&r value in there. If at any point in the future, you want to change that value, you would have to rewrite your program, unless that value was &oproxied&r, in which case you would simply have to update the value in the &lProxy&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"For this tutorial, create materialized and proxied &lVariable Cards&r.\",\n\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"Advanced Operations\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"As you may have noticed during the last tutorial, the &lLogic Programmer&r allows you to do way more than just adding numbers. In this tutorial, we’ll look into the more advanced datastructures and chaining of operators.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"Let’s start by creating a static &7List&0 of the &6Integers&0 &61&0, &610&0 and &6100&0, and visualize it in a &lDisplay Panel&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7Items&0 are one of the possible value types in this mod. Let’s make a static &7Item&0 &lVariable Card&r, dynamically retrieve the mod name from that item, and visualize it in a &lDisplay Panel&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"Remember that you’ll have to use a &lVariable Store&r to store your referenced variable.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text5\": \"For the final part of this tutorial, we’ll combine dynamic variables with operators. Attach an &lEntity Reader&r to a network, read the target &7Entity&0, retrieve the &7Block&0 the target &7Entity&0 is looking at, and visualize that &7Block&0 in a &lDisplay Panel&r.\",\n\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"The Value of Operators\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text1\": \"In &1Integrated Dynamics&0, &2Operators&0 are first-class citizens. Up until now, we have only used them manually using the &lLogic Programmer&r, but in this tutorial, we’ll look into how they can be used in a more dynamic way, by storing &2Operators&0 in &lVariable Cards&r, and invoking them dynamically.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"As a first step, go to the &lLogic Programmer&r, and create a new static &2Operator&0, by starting to type &oArithmetic Addition&r &2Operator&0 in the autocomplete dropdown-list and clicking it, and write it to a &lVariable Card&r. After that, create two &6Integer&0 variables. Next, find the &oApply&r operator, and apply the first &6Integer&0 to the &2Operator&0, which will &obind&r the first input argument of the addition &2Operator&0 to that &6Integer&0, and will output a new &2Operator&0 with one remaining input argument. Next, use the &oApply&r operator again to &obind&r the final input argument of the newly created &2Operator&0. Finally, use a &lDisplay Panel&r to visualize your dynamically invoked addition.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"Next to applying &2Operators&0, there are a lot of other advanced &2Operator&0 operations. We’ll now look into using &2Operators&0 on &7Lists&0, more specifically the &oFilter&r &2Operator&0 that takes a &7List&0 and a &oPredicate&r (an &2Operator&0 with one input argument and a &9Boolean&0 output value). Start by creating a &7List&0 of the &6Integers&0 &61&0, &610&0 and &6100&0. Next, store the &oRelational Equals&r &2Operator&0 in a variable. Next, apply this operator on the &6Integer&0 &610&0. Next, use the &oFilter&r &2Operator&0 with your &7List&0 and the applied &2Operator&0. This will filter away all the items from the &7List&0 that return &9False&0 by applying them on the given operator, which in this case means that all elements that are not equal to &610&0 will be removed from the list. Visualize the result in a &lDisplay Panel&r.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"There are plenty of other operations on &2Operators&0, such as combining &oPredicates&r, flipping &2Operator&0 arguments, and more operations on &7Lists&0.\",\n\n    \"info_book.integrateddynamics.tutorials.challenges\": \"Challenges\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"This final tutorial section contains several non-trivial challenges to show that you understand the basics of &1Integrated Dynamics&0. No further information will be given for each challenge, so good luck!\",\n\n    \"info_book.integrateddynamics.manual\": \"Manual\",\n\n    \"info_book.integrateddynamics.manual.introduction\": \"Introduction\",\n    \"info_book.integrateddynamics.manual.introduction.text1\": \"This part of the book contains detailed descriptions of all parts of the mod.\",\n\n    \"info_book.integrateddynamics.manual.prerequisites\": \"Prerequisites\",\n\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"Menril Trees\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"You may occasionally encounter tall blue trees. Such trees are called &lMenril Trees&r, and have some very interesting properties. First, the leaves give off light. This is caused by the illuminating berries, which are also very tasty! Secondly, the nice-looking wood of the tree contains a lot of resin, which can be used for crafting most of the things in this mod.\",\n\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"Meneglin Biome\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"&lMenril Trees&r are unfortunately quite hard to find. Luckily, there is one biome, the &lMeneglin Biome&r in which these trees thrive. The large amount of &lMenril Trees&r causes some things in this biome to become blue, which is because of the &lMenril Resin&r that is given off by these trees to the environment.\",\n\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"Squeezer\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer.text1\": \"The &lSqueezer&r is an early-game machine that allows you to jump on blocks and items to break or squeeze them. Not only players, but any kind of entity can jump or fall on it to perform the squeezing process. Resulting items will either fall into any adjacent inventory or on the ground. Resulting fluids will remain in the machine and will flow to one of the two sides when there is a target tank. The machine contains a small suspension that can be enabled with a redstone pulse to reset it. Most notably, this machine can be used to squeeze the resin out of &lMenril Logs&r.\",\n\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"Drying Basin\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"Drying out blocks and items can be done using the &lDrying Basin&r. This can for instance be useful when you want to let your &lMenril Resin&r dry into &lCrystallized Menril&r.\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"Whatever you do, don’t insert hot fluids!\",\n\n    \"info_book.integrateddynamics.manual.networks\": \"Networks\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"&lIntegrated Dynamics&r &lnetworks&r are the basis of any good automation system. A network consists of a collection of connected &lLogic Cables&r and parts can be placed on them. These cables should be managed using a &lWrench&r. Shift+right-clicking a cable or part will remove it, while a regular right-click will disconnect it from other cables.\",\n    \"info_book.integrateddynamics.manual.networks.text2\": \"Even though &lLogic Cables&r may look nice, in some cases you’ll probably want to hide them. You can do this using &lFacades&r, which can resemble any material. Furthermore, these cables are also compatible with &lMC MultiPart&r, which means that you can hide your cables in more complex facade structures.\",\n\n    \"info_book.integrateddynamics.manual.logic\": \"Logic\",\n\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"Value Types\",\n\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean\": \"Boolean\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"The simplest value type, its values can be either &9True&0 or &9False&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer\": \"Integer\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"A value type for representing positive and negative numbers. For example &6-20&0, &60&0, &61000&0, &6333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"The values can range between &6–2147483648&0 and &62147483647&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"This value type is part of the &lNumber&r category.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double\": \"Double\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"Real numbers, which can also be positive or negative. For example &e3.1415&0, &e4815162342&0, &e3.33333333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"This value type is part of the &lNumber&r category.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long\": \"Long\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"The same as &6Integers&0, but with a larger value range, which goes from &e-9223372036854775808&0 to &e9223372036854775807&0\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"This value type is part of the &lNumber&r category.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string\": \"String\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"A sequence of characters, for example &4”aaa”&0, &4“Hi!”&0, &4“x!(àà)ç”&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list\": \"List\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list.text1\": \"A list containing values of a certain &lValue Type&r. All elements in a certain list can only contain values of the same value types. For example &8(0, 1, 2, 3)&0, &8(“a”, “b”, “c”)&0, &8(3.33, 1.14, 5, 6)&0\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator\": \"Operator\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"A value containing a reference to an &2Operator&0. Operators can be partially applied for more flexible evaluations based on the concept of &lCurrying&r. This is part of the concept of &lmetaprogramming&r (look it up, it's a thing!), where operators are treated as data by other operators. This basically allows you to create new operators yourself, which can then be used for more complex operations.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"You can for example store the addition &2Operator&0 in a variable card. Do this by starting to type the operator name and selecting it from the autocomplete dropdown-list and clicking it. You can then partially apply it with one &6Integer&0 &lVariable Card&r, which will result in a new virtual operator with one input type and one output type, which internally contains a reference to that applied &6Integer&0. Applying another &6Integer&0 will result in an &6Integer&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"Other operations of &2Operators&0 are present in the &lLogic Programmer&r for more advanced evaluations, such as reducing a list to a single value, or filtering a list based on a &lPredicate&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"Certain things in &lMinecraft&r internally use the &3NBT&0 datastructure. An &3NBT&0 tag is a special kind of object that can hold multiple values of different types. For example the NBT representation of a &8Chest&0 containing all its items, the enchantment information of an &8Pickaxe&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"Variables can hold references to such &3NBT&0 tags, and corresponding operators exist to read or extend these tags.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"&3NBT&0 tags are typically used in this mod as a low-level fallback mechanism, in case you can not read certain information using dedicated operators.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block\": \"Block\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"A reference to a &lMinecraft&r block. For example &8Dirt&0, &8Cobblestone&0, &8Red Stained Clay&0, &8Chest&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"Item\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"A reference to a &lMinecraft&r item. For example &8Apple&0, &8Cobblestone&0, &8Diamond Pickaxe&0, &8Enchanted Sword&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"Entity\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"A reference to a certain &lMinecraft&r entity. For example &8Zombie&0, &8Cow&0, &8Player_abc&0 ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"Fluid\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"A reference to a &lMinecraft&r fluid. For example &8Lava@1000mB&0, &8Water@3mB&0, &8Menril Resin@3030mB&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"Ingredients\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients.text1\": \"A collection of &lItems&r, &lFluids&r and &lEnergy amounts&r (&6Integer&0). Its main use is for representing the input and output of &8Recipes&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"Recipe\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text1\": \"An abstract crafting recipe, that takes &8Ingredients&0 as input and outputs another &8Ingredients&0 value.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text2\": \"Recipes can be created visually using the &lLogic Programmer&r, or they can be read from (supported) machines using the &lMachine Reader&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text3\": \"These recipes can be used for autocrafting mods such as &lIntegrated Crafting&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text4\": \"To quickly find specific recipes among existing &lVariable Cards&r inside a GUI, you can hold shift to temporarily display what the output item of the recipe is.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text5\": \"Shift-clicking on an input slot inside the &lLogic Programmer&r allows you to configure detailed properties of this input slot. For example, it allows you to indicate if NBT should be considered or not, if this item can be reused across multiple recipes (e.g. if it will not be fully consumed after the crafting job), and if tag-based alternatives should be considered for this item.\",\n\n    \"info_book.integrateddynamics.manual.logic.variables\": \"Variables\",\n\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"Variable Card\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&lVariable Cards&r are the primary mode of logic transport in this mod. A variable contains a reference to a certain &lValue&r with a certain &lValue Type&r.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text2\": \"Variables can either be static or dynamic. Static variables are defined once, and will never change afterwards. Dynamic variables are the result of some form of &lLogic Evaluation&r, which are done by &lOperators&r or &lReader Aspects&r. An example of a dynamic variable is the time in the &lMinecraft&r world, which changes continuously.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text3\": \"Dynamic variables will only be evaluated when their value is requested in some way, such as displaying the value in a &lDisplay Panel&r, or being requested as part of the evaluation of another dynamic variable.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"Each &lVariable Card&r has a unique ID, which is used internally in networks for &lLogic Evaluation&r. When an error occurs in the network, these ID’s will be shown to the player to help with debugging. For better usability, a label can be given to &lVariable Cards&r using a &lLabeler&r, which will allow players to see the label instead of the ID’s.\",\n\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"Variable Store\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"Some dynamic variables are evaluated based on other variables, where access to those referenced variables must be possible. A dynamic variable representing the addition of two other variables must be able to access those two other variables when it is evaluated. Access to those other variables must be possible within the same network as where the dynamic variable is being evaluated, which can be done by placing the other variable cards in a &lVariable Store&r. Multiple &lVariable Stores&r can be present in a network. Those other variables may also reference certain variables, which should also be present in that same network.\",\n\n    \"info_book.integrateddynamics.manual.logic.advanced\": \"Advanced\",\n\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path\": \"NBT Path\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text1\": \"When working with complex &3NBT&0 tags, you can quickly require complex operator chains for reading deeply nested NBT values. In order to simplify this task, &4String&0-based &lNBT Path expressions&r can be used to determine the path within an &3NBT&0 tag.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text2\": \"The simplest NBT Path expression is made up of a chain of &lfield selectors&r, such as &4.fieldName&0 or &4[”fieldName”]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text3\": \"For example, applying the expression &4”.root.child1.child2”&0 on the &3NBT&0 tag &3{ root: { child1: { child2: ”some value” }}}&0 will output &3”some value”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text4\": \"Multiple fields can be selected by providing an array of field names or a wildcard, for example &4[fieldA,fieldB]&0 or &4*&0\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text5\": \"For example, applying the expression &4”.a[b1,b2]*”&0 on the &3NBT&0 tag &3{ a: { b1: { c: ”some value” }}}&0 will output &3”some value”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text6\": \"When working with NBT lists, specific entries can be selected by specifying an index, such as &4[1]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text7\": \"For example, applying the expression &4”.a[1]”&0 on the &3NBT&0 tag &3{ a: [10,20] }&0 will output &3”20”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text8\": \"Multiple list indexes can be selected using the slice operator: &4[start:end:step]&0. (Only start is required, the other elements may be omitted)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text9\": \"For example, applying the expression &4”.a[1:4:2]”&0 on the &3NBT&0 tag &3{ a: [0,1,2,3,4,5] }&0 will output &3[1,3]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text10\": \"More advanced filter expressions are also possible using the filter syntax, for example &4[?(@.childName < 10)]&0 (@ refers to the current tag, .. refers to the parent tag, $ refers to the root tag)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text11\": \"For example, applying the expression &4”$.a[?(@ == 3)]”&0 on the &3NBT&0 tag &3{ a: [0,1,2,3,4,5] }&0 will output &3[3]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text12\": \"Expressions can be combined using ”&&” (and), ”||” (or), and ”!” (not).\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text13\": \"For example, applying the expression &4”$.a[?(@ == 3 || (@ == 5))]”&0 on the &3NBT&0 tag &3{ a: [0,1,2,3,4,5] }&0 will output &3[3,5]&0.\",\n\n    \"info_book.integrateddynamics.manual.parts\": \"Parts\",\n\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"Introduction\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text1\": \"Parts can be placed on each side of a &lLogic Cable&r. This section describes parts that can read &lValues&r and make them available with a &lVariable Card&r, and parts that can perform actions based on the &lValues&r within &lVariable Cards&r.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text2\": \"Most parts have configurable settings, which can be accessed using the top-left button in the GUI of a part. This allows you to change the part &lTicks/Operation&r, meaning per how many ticks this part should perform its actions. Secondly, the &lPriority&r of part can be changed, which indicates the order in which this part can act relative to the other parts in the same network in the same tick. If part A has a priority of -1, and part B has a priority of 1, then part B will always perform its actions before part A. If priorities of parts are the same, then the relative order is random.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text3\": \"A part's &lEnergy Channel&r determines where it can draw its energy from. -1 is the wildcard channel (it will match all channels), and 0 is the default channel (used by parts such as energy cells that don't have a channel setting). Finally, &lTarget Side&r determines which side of its target block will be interacted with. This defaults to the side that it's actually placed against.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text4\": \"Some reader and writer parts only contain aspects when certain other mods are present. If no aspects are present for a certain part, a warning will be added to the item tooltip whenever you attempt to craft it.\",\n\n    \"info_book.integrateddynamics.manual.parts.settings\": \"Settings\",\n    \"info_book.integrateddynamics.manual.parts.settings.text1\": \"Most parts have a &lSettings&r screen using which basic part settings can be modified.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text2\": \"The Target Side is the side that this part will interact with. It defaults to the opposite of the side of the cable this part was placed upon.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text3\": \"The Ticks/Operation allows you to configure the ticking frequency of this part. The higher, the slower it operates.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text4\": \"The Priority determines the order in which this part is executed within a single network tick.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text5\": \"The Energy Channel indicates the channel from which energy should be consumed when this part ticks. This is only applicable if energy consumption for networks is enabled.\",\n\n    \"info_book.integrateddynamics.manual.parts.offsets\": \"Offsets\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text1\": \"By default, parts will target the direct neighboring position. However, when &lPart Enhancements&r are applied, the target position can be changed through the part's &lOffset&r screen.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text2\": \"Offsets are always relative to the part's target position, and are limited by the number of Part Enhancements that have been applied.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text3\": \"Offsets can either be set manually, or they can be set visually using the &lWrench&r. When setting the &lWrench&r to offset-mode, a position can be stored by shift + right-clicking. This position can be saved into parts by right-clicking them with that &lWrench&r.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text4\": \"Offsets can not only be statically defined, buy they can also be &odynamic&r using &lVariable Cards&r.\",\n\n    \"info_book.integrateddynamics.manual.parts.reader\": \"Readers\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"Introduction\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction.text1\": \"The simplest method for creating a dynamic variable is done using &lReaders&r. Different types of readers exist for reading information from the world and storing it inside a &lVariable Card&r. Each &lReader&r contains one or more &lAspects&r for reading different things with different &lValue Types&r.\",\n\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"Audio\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"Information about music and audio that is playing in the enviroment.\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"Block\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"Information about the block the part is facing.\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"Entity\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"Information about the entity or entities that are inside the square that the part is facing.\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"Extra-Dimensional\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"Information about the server, such as the list of online players.\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"Fluid\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"Read values from the fluid tank this part is facing.\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"Inventory\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"Read information from the facing inventory, like the amount of slots and items. This will not transfer items itself.\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"Machine\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"The machine this part is facing can be read, such as the work they are doing.\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"Network\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"Read information about the &lIntegrated Dynamics&r network this part is facing. This does not have to be the same network as the network this part is on.\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"Redstone\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"Read redstone information from the facing block.\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"World\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"Information about the world this part is in, like the current weather and time.\",\n\n    \"info_book.integrateddynamics.manual.parts.writer\": \"Writers\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"Audio\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"Play sounds and music.\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"Effect\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"Emit particles based on certain values and settings.\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"Entity\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"Configure and control entities.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"Machine\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"Configure and control machines.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"Inventory\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"Perform actions related to inventories and pipes. This will not transfer items itself.\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"Redstone\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"Output a certain redstone level.\",\n\n    \"info_book.integrateddynamics.manual.parts.other\": \"Others\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"Light\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"A simple light that is either always on, or can be controlled using a &1Boolean&0 or &6Integer&0 &lVariable Card&r.\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"Display\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"A useful monitoring or debugging tool that simply shows the value of a &lVariable Card&r. &lDisplays&r can be rotated by right-clicking with a &lWrench&r.\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text2\": \"Inside the gui, a &lCopy&r button is available to copy the currently displayed value to your clipboard so that it can be used outside of the game. You can also just use the default copy hotkey of your OS to copy this value.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"Mono-Directional Connector\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"Sometimes, your logic networks will span large distances. While regular cables will do the trick, it may not be very convenient to place cables across a long distance.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&lMono-Directional Connectors&r allow you to connect networks wirelessly over a long distance. This can be done by placing a first connector on one network, and a second connector on another network. These two connectors must be in a straight line to each other, with a distance less than or equal to 512 blocks. The connectors will appear active when a valid connection has been made.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"Omni-Directional Connector\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"While &lMono-Directional&r connectors are useful for long-distance connections in a single direction, in some cases this might be a bit too limited.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"You may for example want to connect more than two networks together using a single connector or connect with networks in other dimensions. For these cases, you can use the &lOmni-Directional&r connectors.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"These connectors will have a unique group id when first crafted, which identifies the group in which networks can be connected. Each connector group has a unique color of particles when placed in the world, the group id can be seen in the item tooltip or by right-clicking on the connector when placed. Additional connectors can be added to a group by crafting a new one using an existing connector within a group.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"You can also change the group of existing connectors in two ways. Crafting one alone in a crafting table will assign it to a new group with no other members, and crafting two together will copy the first one's group to the second one.\",\n\n    \"info_book.integrateddynamics.manual.machines\": \"Machines\",\n\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"Materializer\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"In order to make a dynamic &lVariable Card&r static, a &lMaterializer&r can be used. The dynamic &lVariable Card&r must be inserted, and a new empty &lVariable Card&r must be inserted, which will cause it to contain the current value of the dynamic &lVariable Card&r as a static value.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"This can be useful in situations where you want to log the values of a certain dynamic &lVariable Card&r, or when you don’t need the dynamic evaluation of a &lVariable Card&r anymore.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&lVariable Cards&r can be piped in and out the &lMaterializer&r.\",\n\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"Proxy\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"A &lProxy&r can be used to make a dynamic reference to a certain &lVariable Card&r. It allows you to insert a &lVariable Card&r in the top slot, and write a new &lVariable Card&r, which will dynamically take the value of the &lVariable Card&r that is present in the proxy. Note that the referenced (or any other) &lVariable Card&r must remain in the top slot for the proxy to work.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"This is useful in situations where you have a chain of &lVariable Cards&r, but you still would like to be able to change one value manually. You could then proxy that variable, which will allow it to be swapped with another variable at any time.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"Just as with the &lMaterializer&r, &lVariable Cards&r can be piped in and out of it.\",\n\n    \"info_book.integrateddynamics.manual.machines.delay\": \"Delayer\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"In some situations, you might need to maintain some kind of history of variable values. When you for instance want to take the average or difference over the last values.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text2\": \"For this, you can use a &lDelayer&r, with which you can maintain the history of the values of a given variable. You can do this by inserting a &lVariable Card&r in the top slot for which you want to maintain the history. You can now create &lVariable Cards&r that will refer to a list of values, which corresponds to the history. The referenced (or any other) &lVariable Card&r must remain in the top slot for the delayer to work.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text3\": \"The newest values correspond to the last elements in the list. You can choose the maximum length of the history from within the gui.\",\n\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"Energy Battery\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"&lIntegrated Dynamics&r networks can be configured to consume energy. For this, you can attach &lEnergy Batteries&r to the network to add energy storage capacity for it.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text2\": \"These batteries accept &lRF&r-based power.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"Applying a redstone signal will enable auto-export mode of energy to all sides.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"When this battery is held in your inventory, &lauto-supply&r mode can be enabled by shift+right clicking. When enabled, this battery will automatically try to fill the items you are holding in your two hands with energy.\",\n\n    \"info_book.integrateddynamics.manual.machines.generator\": \"Generator\",\n    \"info_book.integrateddynamics.manual.machines.generator.text1\": \"When you need &lRF&r energy, you can use the &lGenerator&r to create energy from &oburnable&r items.\",\n\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"Mechanical Squeezer\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer.text1\": \"There will come a time, when you will have had enough of all this jumping on &lSqueezers&r. If you haven't automated your &lSqueezers&r by then, perhaps it's time to look into making a &lMechanical Squeezer&r. It does not require any manual effort, but it requires energy to run. This also makes it significantly faster than the regular &lSqueezers&r and allows more things to be squeezed with a higher yield.\",\n\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"Mechanical Drying Basin\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"Drying with the &lDrying Basin&r can take a while. If you want things to go faster, or if you want to be able to dry more things, you can use the &lMechanical Drying Basin&r, which requires energy to run.\",\n\n    \"info_book.integrateddynamics.manual.logic_programming\": \"Logic Programming\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"Another method for creating dynamic variable cards, is by applying an &lOperator&r on one or more &lVariable Cards&r to produce a new &lVariable Card&r. An example of an &lOperator&r is the addition of two &lNumbers&r, resulting in a new &lNumber&r.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text2\": \"An &lOperator&r can have one or more input values, and has one output value. In most cases the inputs and output have a certain &lValue Type&r, otherwise, they will have the &lAny&r value type. An &lOperator&r with a single input and one output &lBoolean&r is called a &lPredicate&r.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"The &lLogic Programmer&r contains a large collection of &lOperators&r. It also allows you to create static &lVariable Cards&r for all &lValue Types&r. After selecting an &lOperator&r, valid &lVariable Cards&r should be inserted in the expected input slots. Finally, another &lVariable Card&r should be inserted in the output slot. If the &lOperator&r is valid, a new dynamic variable will be written to the card. Remember that the referenced &lVariable Cards&r have to be present in a &lVariable Store&r in the same network where the &lOperator&r will be evaluated.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text4\": \"Besides writing values to &lVariable Cards&r, the &lLogic Programmer&r can also be used to modify existing &lVariable Cards&r. The value of an existing &lVariable Card&r can be loaded into the &lLogic Programmer&r by inserting it into the slot on the right-hand side while making sure no &lOperators&r or &lValue Type&r on the left-hand side is selected (which is the case when first opening the &lLogic Programmer&r, or the current element can be cleared).\",\n    \"info_book.integrateddynamics.manual.logic_programming.text5\": \"The &lLogic Programmer&r allows you to search through the list of &lOperators&r by name using the textbox on the top. It also allows you to filter on input or output value types, by inserting a &lVariable Card&r of those types in the bottom-left slots.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text6\": \"If a &lLabeller&r is present in the player’s inventory, the resulting &lVariable Card&r can be labelled immediately by clicking on the ‘E’ button next to the output slot.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text7\": \"The &lLogic Programmer&r exists both in block-form and item-form. Both can be interchanged by placing them in a crafting grid.\",\n\n    \"info_book.integrateddynamics.manual.diagnostics\": \"Network Diagnostics\",\n    \"info_book.integrateddynamics.manual.diagnostics.text1\": \"Players with operator status have access to the &lNetwork Diagnostics&r tool, which allows the tick time across all networks on the server to be inspected.\",\n    \"info_book.integrateddynamics.manual.diagnostics.text2\": \"To enable this tool, the command &o\\\\integrateddynamics networkdiagnostics&r can be executed, which will start a temporary local web server.\",\n    \"info_book.integrateddynamics.manual.diagnostics.text3\": \"When clicking on the link shown after executing the command, your Web browser will open, and will show a table containing all networks and their elements. You can click on any row to highlight them in the world, or click on the &lTeleport&r button to teleport your player to them.\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/es_es.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/fi_fi.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/fr_fr.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/he_il.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/hu_hu.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/it_it.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/ja_jp.json",
    "content": "{\n    \"_comment\": \"------Integrated Dynamicsの内容について------\",\n    \"itemGroup.integrateddynamics\": \"Integrated Dynamics\",\n    \"general.integrateddynamics.description\": \"装置を完全かつ自動的に制御します。\",\n    \"general.integrateddynamics.info.none\": \"無し\",\n    \"general.integrateddynamics.energy\": \"エネルギー\",\n    \"general.integrateddynamics.energy_unit\": \"RF\",\n    \"general.integrateddynamics.error.no_network\": \"有効なネットワークが見つかりません\",\n    \"general.integrateddynamics.true\": \"True\",\n    \"general.integrateddynamics.false\": \"False\",\n    \"gui.integrateddynamics.part_settings\": \"パーツ設定\",\n    \"gui.integrateddynamics.aspect_settings\": \"アスペクト設定\",\n    \"gui.integrateddynamics.part_offsets\": \"パーツ補正\",\n    \"gui.integrateddynamics.keybinding\": \"キー割り当て\",\n    \"gui.integrateddynamics.button.save\": \"保存\",\n    \"gui.integrateddynamics.button.rename\": \"リネーム\",\n    \"gui.integrateddynamics.button.copy\": \"コピー\",\n    \"gui.integrateddynamics.button.copy.info\": \"クリップボードにコピー\",\n    \"gui.integrateddynamics.button.edit\": \"編集\",\n    \"gui.integrateddynamics.button.add\": \"追加\",\n    \"gui.integrateddynamics.button.remove\": \"削除\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"ティック/演算\",\n    \"gui.integrateddynamics.partsettings.priority\": \"優先度\",\n    \"gui.integrateddynamics.partsettings.channel\": \"エネルギーチャンネル\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"このサーバーではネットワークのエネルギー消費が無効になっています。\",\n    \"gui.integrateddynamics.partsettings.side\": \"対象位置\",\n    \"gui.integrateddynamics.partoffset.offsets\": \"対象位置に対するオフセット\",\n    \"gui.integrateddynamics.partoffset.offsets.max\": \"最大オフセット: %s\",\n    \"gui.integrateddynamics.partoffset.offsets.max.howtoincrease\": \"パーツ拡張: オフセットで上限を増やします\",\n    \"gui.integrateddynamics.partoffset.slot.message.noint\": \"無効な整数座標\",\n    \"gui.integrateddynamics.partoffset.slot.message.outofrange\": \"オフセットが許容範囲を超えています\",\n    \"gui.integrateddynamics.partoffset.x\": \"X軸補正\",\n    \"gui.integrateddynamics.partoffset.y\": \"Y軸補正\",\n    \"gui.integrateddynamics.partoffset.z\": \"Z軸補正\",\n    \"gui.integrateddynamics.delay.capacity\": \"容量\",\n    \"gui.integrateddynamics.part.properties\": \"プロパティ:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"フィルター\",\n    \"gui.integrateddynamics.rewards\": \"報酬\",\n    \"gui.integrateddynamics.rewards.collect\": \"受領\",\n    \"gui.integrateddynamics.rewards.collected\": \"受領済み\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"報酬を受け取れません。インベントリがいっぱいですか？\",\n    \"gui.integrateddynamics.input\": \"入力: %s\",\n    \"gui.integrateddynamics.output\": \"出力: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"流体を自動排出\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"流体を自動排出: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"隣接するタンクに送信します。\",\n    \"gui.integrateddynamics.input_short\": \"入力\",\n    \"gui.integrateddynamics.output_short\": \"出力\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"厳格なNBT\",\n    \"gui.integrateddynamics.recipe.tagvariants\": \"アイテムタグの種類\",\n    \"gui.integrateddynamics.recipe.reusable\": \"再利用可能\",\n    \"gui.integrateddynamics.operator.globalname\": \"グローバル名: %s\",\n    \"gui.integrateddynamics.operator.localname\": \"メソッド名: %s\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Integrated Dynamics - ネットワーク診断\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"パーツ\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"ストレージオブザーバー\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"ネットワーク\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"ケーブル\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"パーツ\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"ティック時間(ミリ秒)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"ディメンション\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"位置\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"側面\",\n    \"gui.integrateddynamics.diagnostics.table.actions\": \"動作\",\n    \"key.categories.integrateddynamics\": \"Integrated Dynamics\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"ロジックプログラマーの検索フォーカス\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"ロジックプログラマーの変数リネーム\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"ページがめくられる\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"複数のページがめくられる\",\n    \"block.integrateddynamics.cable\": \"ロジックケーブル\",\n    \"block.integrateddynamics.variablestore\": \"変数ストア\",\n    \"block.integrateddynamics.energy_battery\": \"エネルギーバッテリー\",\n    \"block.integrateddynamics.energy_battery.info\": \"他のエネルギーバッテリーと一緒にクラフトグリッドに配置すると、容量が増加します。Shiftキーを押しながら右クリックすると、自動的に供給されます。\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"自動供給: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"クリエイティブエネルギーバッテリー\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"自動供給: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"ロジックプログラマー\",\n    \"block.integrateddynamics.coal_generator\": \"発電機\",\n    \"block.integrateddynamics.coal_generator.info\": \"燃料アイテムからエネルギーを生成します\",\n    \"block.integrateddynamics.proxy\": \"プロキシ\",\n    \"block.integrateddynamics.proxy.info\": \"変数参照をアスペクトとして公開します。クラフトグリッドでクリアします。\",\n    \"block.integrateddynamics.materializer\": \"マテリアライザー\",\n    \"block.integrateddynamics.materializer.info\": \"変数の値を新しい変数に具現化します\",\n    \"block.integrateddynamics.menril_log\": \"メンリルの原木\",\n    \"block.integrateddynamics.menril_log_filled\": \"濃縮されたメンリルの原木\",\n    \"block.integrateddynamics.menril_log_stripped\": \"樹皮を剥いだメンリルの原木\",\n    \"block.integrateddynamics.menril_leaves\": \"メンリルの葉\",\n    \"block.integrateddynamics.menril_sapling\": \"メンリルの苗木\",\n    \"block.integrateddynamics.menril_planks\": \"メンリルの板材\",\n    \"block.integrateddynamics.menril_slab\": \"メンリルのハーフブロック\",\n    \"block.integrateddynamics.menril_fence\": \"メンリルのフェンス\",\n    \"block.integrateddynamics.menril_fence_gate\": \"メンリルのフェンスゲート\",\n    \"block.integrateddynamics.menril_wood\": \"メンリルの木\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"樹皮を剥いだメンリルの木\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"結晶化したメンリルブロック\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"結晶化したメンリルレンガ\",\n    \"block.integrateddynamics.block_menril_resin\": \"メンリル樹脂\",\n    \"block.integrateddynamics.drying_basin\": \"乾燥槽\",\n    \"block.integrateddynamics.drying_basin.info\": \"アイテムや流体の乾燥用\",\n    \"block.integrateddynamics.squeezer\": \"圧搾器\",\n    \"block.integrateddynamics.squeezer.info\": \"ジャンプすることで圧迫して平らにして押し潰します。レッドストーン信号でリセットします。\",\n    \"block.integrateddynamics.menril_torch\": \"メンリルの松明\",\n    \"block.integrateddynamics.menril_torch_stone\": \"メンリルストーンの松明\",\n    \"block.integrateddynamics.invisible_light\": \"不可視の明かり\",\n    \"block.integrateddynamics.menril_door\": \"メンリルのドア\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"メンリルの階段\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"結晶化したメンリルの階段\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"結晶化したメンリルレンガの階段\",\n    \"block.integrateddynamics.delay\": \"ディレイヤー\",\n    \"block.integrateddynamics.delay.info\": \"変数の履歴をアスペクトとして公開します。クラフトグリッドでクリアします。\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"液体コーラス\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"結晶化したコーラスブロック\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"結晶化したコーラスレンガ\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"結晶化したコーラスの階段\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"結晶化したコーラスレンガの階段\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"機械式圧搾器\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"エネルギーを使用して圧迫して平らにして押し潰します。\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"機械式乾燥槽\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"エネルギーを使用してアイテムや流体を乾燥します。\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"結晶化したメンリルのハーフブロック\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"結晶化したメンリルレンガのハーフブロック\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"結晶化したコーラスのハーフブロック\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"結晶化したコーラスレンガのハーフブロック\",\n    \"item.integrateddynamics.general.id\": \"§e§lID: §r%s\",\n    \"item.integrateddynamics.wrench\": \"レンチ\",\n    \"item.integrateddynamics.wrench.info\": \"空中でShift+右クリックするとモードが変わります\",\n    \"item.integrateddynamics.wrench.mode\": \"§eモード: §r%s\",\n    \"item.integrateddynamics.wrench.mode.default\": \"デフォルト\",\n    \"item.integrateddynamics.wrench.mode.default.info\": \"パーツを削除したりブロックを回転したりします\",\n    \"item.integrateddynamics.wrench.mode.offset\": \"オフセットを保存\",\n    \"item.integrateddynamics.wrench.mode.offset.info\": \"Shiftキーを押しながら位置を右クリックし、オフセットをリンクするパーツを右クリックします\",\n    \"item.integrateddynamics.wrench.mode.offset.saved\": \"位置はレンチに保存されました: %s\",\n    \"item.integrateddynamics.wrench.mode.offset.success\": \"新しいオフセット位置がパーツに設定されました\",\n    \"item.integrateddynamics.wrench.mode.offset.incomplete\": \"このレンチにはまだ位置が保存されていません\",\n    \"item.integrateddynamics.wrench.mode.offset.fail\": \"オフセットを設定できませんでした。拡張機能を使用してオフセットを増やしてください！\",\n    \"item.integrateddynamics.wrench.mode.offset.pos\": \"位置: %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side\": \"オフセットと側面を保存\",\n    \"item.integrateddynamics.wrench.mode.offset_side.info\": \"Shiftキーを押しながら位置を右クリックし、オフセットと側面をリンクするパーツを右クリックします\",\n    \"item.integrateddynamics.wrench.mode.offset_side.saved\": \"位置と側面はレンチに保存されました: %s - %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side.success\": \"新しいオフセット位置と側面がパーツに設定されました\",\n    \"item.integrateddynamics.wrench.mode.offset_side.side\": \"側面: %s\",\n    \"item.integrateddynamics.variable\": \"変数カード\",\n    \"item.integrateddynamics.variable.info\": \"クラフティンググリッドを消去またはコピー\",\n    \"item.integrateddynamics.variable.warning\": \"§4§l警告: このアイテムを中クリックしてコピーしないでください！\",\n    \"item.integrateddynamics.variable.id\": \"§e§o変数ID: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"ラベラー\",\n    \"item.integrateddynamics.labeller.info\": \"変数にラベルを付けます。ロジックプログラマーでも動作します。\",\n    \"item.integrateddynamics.labeller.button.write\": \"書き込み\",\n    \"item.integrateddynamics.facade\": \"外装\",\n    \"item.integrateddynamics.facade.info\": \"作業台でブロックと組み合わせます\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"液体コーラス入りバケツ\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"メンリル樹脂入りバケツ\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"結晶化したメンリル塊\",\n    \"item.integrateddynamics.variable_transformer\": \"入出力変数変換器\",\n    \"item.integrateddynamics.variable_transformer_output\": \"出力変数変換器\",\n    \"item.integrateddynamics.variable_transformer_input\": \"入力変数変換器\",\n    \"item.integrateddynamics.menril_berries\": \"メンリルベリー\",\n    \"item.integrateddynamics.menril_berries.info\": \"美味しくて明るい\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"ポータブルロジックプログラマー\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"ガイドブック\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"チュートリアル付きの完全なModリファレンスガイド\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"結晶化したコーラス塊\",\n    \"item.integrateddynamics.logic_director\": \"ロジックディレクター\",\n    \"item.integrateddynamics.proto_chorus\": \"原始のコーラス\",\n    \"item.integrateddynamics.enhancement_offset\": \"パーツ拡張: オフセット\",\n    \"item.integrateddynamics.enhancement_offset.info\": \"右クリックでパーツの最大オフセットを増やします\",\n    \"item.integrateddynamics.enhancement_offset.tooltip\": \"オフセット増加: %s\",\n    \"item.integrateddynamics.enhancement_offset.increased\": \"パーツオフセットが%sに増加しました\",\n    \"item.integrateddynamics.enhancement_offset.limit\": \"パーツオフセットは既に制限されています (%s)\",\n    \"fluid_type.integrateddynamics.menril_resin\": \"メンリル樹脂\",\n    \"fluid_type.integrateddynamics.liquid_chorus\": \"液体コーラス\",\n    \"biome.integrateddynamics.meneglin\": \"メネグリン\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"メネグリン発見\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"メネグリンバイオームで最も簡単に見つかるメンリルの木を伐採します。\",\n    \"advancement.integrateddynamics.squeezing\": \"上手に圧搾する\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"圧搾器をクラフトする。\",\n    \"advancement.integrateddynamics.drying\": \"上手に乾かす\",\n    \"advancement.integrateddynamics.drying.desc\": \"乾燥槽をクラフトする。\",\n    \"advancement.integrateddynamics.menril_production\": \"メンリルの生産\",\n    \"advancement.integrateddynamics.menril_production.desc\": \"メンリルの木をメンリル樹脂に加工し、乾燥させて結晶化したメンリルを取得します。\",\n    \"advancement.integrateddynamics.cables_logic\": \"ロジック用ケーブル\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"ロジックケーブルをクラフトする\",\n    \"advancement.integrateddynamics.networks_logic\": \"ロジック用ネットワーク\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"少なくとも10本のロジックケーブルを接続してロジックネットワークを構築します。\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"メンリルレンチング\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"レンチをクラフトする\",\n    \"advancement.integrateddynamics.variables\": \"値用変数\",\n    \"advancement.integrateddynamics.variables.desc\": \"変数カードをクラフトする\",\n    \"advancement.integrateddynamics.variable_input\": \"可変入力変換\",\n    \"advancement.integrateddynamics.variable_output\": \"可変出力変換\",\n    \"advancement.integrateddynamics.redstone_reading\": \"レッドストーンリーディング\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"レッドストーンリーダーをクラフトする\",\n    \"advancement.integrateddynamics.block_reading\": \"ブロックリーディング\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"ブロックリーダーをクラフトする\",\n    \"advancement.integrateddynamics.inventory_reading\": \"インベントリリーディング\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"インベントリリーダーをクラフトする\",\n    \"advancement.integrateddynamics.machine_reading\": \"マシンリーディング\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"マシンリーダーをクラフトする\",\n    \"advancement.integrateddynamics.value_displaying\": \"バリューディスプレイング\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"表示パネルをクラフトする\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"レッドストーンキャプチャ中\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"レッドストーン§6整数§7値の変数カードを作成する。\",\n    \"advancement.integrateddynamics.redstone_observement\": \"レッドストーン観察記録\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"表示パネルに§6整数§7の変数カードを配置する。\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"レッドストーン通信\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"レッドストーンライターにレッドストーンリーダーによって読み取られた最大整数のレッドストーンレベルを書き込む。\",\n    \"advancement.integrateddynamics.recipe_reading\": \"レシピ読み込み中\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"マシンリーダーを使用して、マシン(作業台など)からレシピのリストを読み込む。\",\n    \"advancement.integrateddynamics.recipe_creation\": \"レシピ作成\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"ロジックプログラマーを使用して、視覚的に§8レシピ§7を作成する。\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"レシピ検索\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"チェストのレシピをマシンリーダーで動的に調べる。\",\n    \"advancement.integrateddynamics.lectern_data\": \"書見台NBT\",\n    \"advancement.integrateddynamics.lectern_data.desc\": \"書見台などのブロックリーダーを使用して、マシンからNBTタグを読み取る。\",\n    \"advancement.integrateddynamics.lectern_book\": \"書見台NBT本\",\n    \"advancement.integrateddynamics.lectern_book.desc\": \"ブロックリーダーから読み取ったNBTタグの書見台ブロック内の本のNBTを読み込む。\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"書見台NBT本名\",\n    \"advancement.integrateddynamics.lectern_book_name.desc\": \"ブロックリーダーから読み取ったNBTタグにNBTパス式を適用して、書見台ブロック内の本の名前(ID)を読み込む。\",\n    \"advancement.integrateddynamics.logic_programming\": \"ロジックプログラミング\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"ロジックプログラマーのGUIを開く\",\n    \"advancement.integrateddynamics.constant_definition\": \"定数定義\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"定数の§6整数§7を変数カードに定義する。\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"算数の追加\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"2つの§e数値§7を追加して結果を表示する。\",\n    \"advancement.integrateddynamics.variable_materialization\": \"変数の具現化\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"§lマテリアライザー§rで変数を具現化する。\",\n    \"advancement.integrateddynamics.variable_proxying\": \"変数のプロキシ中\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"§lプロキシ§rに有効なプロキシ変数を作成する。\",\n    \"advancement.integrateddynamics.logical_list_building\": \"ロジカルリスト構築\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"§6整数§7 1, 10, 100 を使って静的なリストを作成する。\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"アイテム起源の識別\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"アイテムのMod名を取得する。\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"私は何を見ているのだろう？\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"見ているブロックを取得する。\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"動的加算\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"加算演算子を動的に呼び出す\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"動的リストフィルタリング\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"整数リスト1、10、100を10にフィルタリングする。\",\n    \"advancement.integrateddynamics.creeper_taming\": \"クリーパー調教\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"表示パネルにクリーパーのHPを表示する\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"スポンジの足音\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"濡れたスポンジブロックの足音を再生する\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"再帰的再帰\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"無限リストを作成し、それを表示パネルに表示する\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"黒曜石用のツール？\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"石のツルハシが黒曜石ブロックを破壊できるかどうかを確認し、その結果を表示パネルに表示する。\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"スマート感圧板\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"ドアを直接指しているレッドストーンライターを使用して、エンティティ名の一致に基づいてエンティティリーダーを通過すると自動的にドアが開くシステムを作成する。(ヒント: エンティティ名を等号演算子の最初の引数として渡します)\",\n    \"valuetype.integrateddynamics.error.value\": \"無効な値\",\n    \"valuetype.integrateddynamics.value_type\": \"値型\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§e型: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§o値: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§e署名: §r\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"入力「%s」はこの値型に対して解析できませんでした。\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"この値型は入力項目を必要とします。\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"要素%sは無効です: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"%sが与えられている間、値型%sを期待しています。\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"位置%sには演算子の値型が必要ですが、%sが指定されました\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"演算子の値%sには値型(%s)が必要ですが、(%s)が指定されました。\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"挿入されたアイテムはブロックではありません。\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"挿入されたアイテムには流体が含まれていません。\",\n    \"valuetype.integrateddynamics.error.no_light_calculator\": \"値型%sは、明るさレベルを導出するために使用することはできません。\",\n    \"valuetype.integrateddynamics.error.deserialize\": \"「%s」のデシリアライズ中に問題が発生しました: %s\",\n    \"valuetype.integrateddynamics.any\": \"いずれか\",\n    \"valuetype.integrateddynamics.any.info\": \"任意の型の値を受け入れます。\",\n    \"valuetype.integrateddynamics.number\": \"数値\",\n    \"valuetype.integrateddynamics.number.info\": \"全ての数値型を受け入れます。\",\n    \"valuetype.integrateddynamics.named\": \"名前付き\",\n    \"valuetype.integrateddynamics.named.info\": \"アイテムやブロックの表示名など、名前があるもの。\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"ユニークな名前付き\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"アイテムやブロックなど、固有の名前を持つもの。\",\n    \"operator.integrateddynamics.string.string_error\": \"エラー\",\n    \"operator.integrateddynamics.string.string_error.info\": \"カスタムエラーをスローする\",\n    \"valuetype.integrateddynamics.boolean\": \"ブール\",\n    \"valuetype.integrateddynamics.integer\": \"整数\",\n    \"valuetype.integrateddynamics.double\": \"ダブル\",\n    \"valuetype.integrateddynamics.long\": \"ロング\",\n    \"valuetype.integrateddynamics.string\": \"文字列\",\n    \"valuetype.integrateddynamics.operator\": \"演算子\",\n    \"valuetype.integrateddynamics.nbt\": \"NBT\",\n    \"valuetype.integrateddynamics.list\": \"リスト\",\n    \"valuetype.integrateddynamics.block\": \"ブロック\",\n    \"valuetype.integrateddynamics.itemstack\": \"アイテム\",\n    \"valuetype.integrateddynamics.entity\": \"エンティティ\",\n    \"valuetype.integrateddynamics.fluidstack\": \"流体\",\n    \"valuetype.integrateddynamics.ingredients\": \"材料\",\n    \"valuetype.integrateddynamics.recipe\": \"レシピ\",\n    \"valuetype.integrateddynamics.ingredients.slot.info\": \"Shiftキーを押しながらクリックして設定\",\n    \"parttype.integrateddynamics.error.low_energy\": \"エネルギーが不足しています\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"エネルギー不足\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"無効\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"エラー:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"アスペクト: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"値: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"グループ: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"警告: 利用可能な機能はありません。他のModがインストールされている場合に使用可能になる可能性があります。\",\n    \"parttype.integrateddynamics.tooltip.maxoffset\": \"最大オフセット: %s\",\n    \"parttype.integrateddynamics.audio_reader\": \"オーディオリーダー\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"オーディオ情報を読み込みます\",\n    \"parttype.integrateddynamics.block_reader\": \"ブロックリーダー\",\n    \"parttype.integrateddynamics.block_reader.info\": \"ブロック情報を読み込みます\",\n    \"parttype.integrateddynamics.entity_reader\": \"エンティティリーダー\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"エンティティ情報を読み込みます\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"拡張ディメンションリーダー\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"全ディメンションで共有されている情報を読み込みます\",\n    \"parttype.integrateddynamics.fluid_reader\": \"流体リーダー\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"流体タンクを読み込みます\",\n    \"parttype.integrateddynamics.inventory_reader\": \"インベントリリーダー\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"インベントリからデータを読み込みます\",\n    \"parttype.integrateddynamics.network_reader\": \"ネットワークリーダー\",\n    \"parttype.integrateddynamics.network_reader.info\": \"対象ネットワークのプロパティを読み込みます\",\n    \"parttype.integrateddynamics.machine_reader\": \"マシンリーダー\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"マシンのプロパティを読み込みます\",\n    \"parttype.integrateddynamics.redstone_reader\": \"レッドストーンリーダー\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"レッドストーンの値を読み込みます\",\n    \"parttype.integrateddynamics.world_reader\": \"ワールドリーダー\",\n    \"parttype.integrateddynamics.world_reader.info\": \"ワールド、ブロック、エンティティに関連するものを読み込みます\",\n    \"parttype.integrateddynamics.audio_writer\": \"オーディオライター\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"オーディオと音楽を出力します\",\n    \"parttype.integrateddynamics.effect_writer\": \"エフェクトライター\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"パーティクルを出力します\",\n    \"parttype.integrateddynamics.entity_writer\": \"エンティティライター\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"エンティティ設定を書き込みます\",\n    \"parttype.integrateddynamics.machine_writer\": \"マシンライター\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"マシン設定を書き込みます\",\n    \"parttype.integrateddynamics.inventory_writer\": \"インベントリライター\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"インベントリへ書き込みます\",\n    \"parttype.integrateddynamics.redstone_writer\": \"レッドストーンライター\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"レッドストーンに書き込みます\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"このパネルは%sを受け付けていません。\",\n    \"parttype.integrateddynamics.static_light_panel\": \"静的ライトパネル\",\n    \"parttype.integrateddynamics.static_light_panel.info\": \"常にアクティブなシンプルなライトパネルです\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"動的ライトパネル\",\n    \"parttype.integrateddynamics.display_panel\": \"表示パネル\",\n    \"parttype.integrateddynamics.display_panel.info\": \"変数の値をテキストとして視覚的に表示します\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"単方向コネクター\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"直線内の距離で他の単方向コネクターに接続できます\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"全方向コネクター\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"異なるディメンションを含むあらゆる場所で他の全方向コネクターに接続できます。\",\n    \"variable.integrateddynamics.error.invalid_item\": \"現在の変数はこの側面では使用できません。\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"ID %sのパーツが現在のネットワーク内で見つかりませんでした。\",\n    \"variable.integrateddynamics.error.recursion\": \"ID %sの変数はそれ自身を参照しています。\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§oプロキシID: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"ID %sのプロキシが現在のネットワーク内で見つかりませんでした。\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"ID %sのプロキシは変数を公開していません。\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"IDが%sのプロキシは型が%sの変数を公開することが予想されますが、%sが見つかりました。\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§oディレイID: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"ID %sのディレイが現在のネットワーク内で見つかりませんでした。\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"ID %sのディレイは変数を公開していません。\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"IDが%sのディレイは型が%sの変数を公開することが予想されますが、%sが見つかりました。\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§eアスペクト: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§e型: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§oパーツID: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"このアスペクトは%sを期待していますが、 %sが与えられています。\",\n    \"aspect.integrateddynamics.error.recursion\": \"%sのアスペクト変数はそれ自体を参照しています。\",\n    \"aspect.integrateddynamics.error.no_value_interface\": \"有効な値のインターフェースが見つかりませんでした。\",\n    \"aspect.integrateddynamics.error.no_value_interface_value\": \"有効な値のインターフェース値が見つかりませんでした。\",\n    \"aspect.integrateddynamics.name\": \"アスペクト\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp\": \"ハープ音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp.info\": \"ハープの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum\": \"バスドラム音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum.info\": \"バスドラムの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"スネア音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"スネアの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat\": \"ハイハット音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat.info\": \"ハイハットの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass\": \"コントラバス音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass.info\": \"コントラバスの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute\": \"フルート音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute.info\": \"フルートの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell\": \"ベル音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell.info\": \"ベルの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar\": \"ギター音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar.info\": \"ギターの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime\": \"チャイム音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime.info\": \"チャイムの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone\": \"木琴音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone.info\": \"木琴の音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone\": \"鉄琴音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone.info\": \"鉄琴の音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell\": \"カウベル音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell.info\": \"カウベルの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo\": \"ディジュリドゥ音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo.info\": \"ディジュリドゥの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit\": \"電子音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit.info\": \"電子音の音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo\": \"バンジョー音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo.info\": \"バンジョーの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling\": \"電子ピアノ音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling.info\": \"電子ピアノの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie\": \"ゾンビ音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie.info\": \"ゾンビの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton\": \"スケルトン音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton.info\": \"スケルトンの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper\": \"クリーパー音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper.info\": \"クリーパーの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon\": \"ドラゴン音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon.info\": \"ドラゴンの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton\": \"ウィザースケルトン音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton.info\": \"ウィザースケルトンの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin\": \"ピグリン音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin.info\": \"ピグリンの音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head\": \"頭音\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head.info\": \"頭の音域を読み込む。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp\": \"ハープ音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp.info\": \"ハープの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum\": \"バスドラム音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum.info\": \"バスドラムの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"スネア音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"スネアの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat\": \"ハイハット音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat.info\": \"ハイハットの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass\": \"コントラバス音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass.info\": \"コントラバスの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute\": \"フルート音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute.info\": \"フルートの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell\": \"ベル音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell.info\": \"ベルの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar\": \"ギター音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar.info\": \"ギターの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime\": \"チャイム音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime.info\": \"チャイムの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone\": \"木琴音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone.info\": \"木琴の音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone\": \"鉄琴音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone.info\": \"鉄琴の音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell\": \"カウベル音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell.info\": \"カウベルの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo\": \"ディジュリドゥ音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo.info\": \"ディジュリドゥの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit\": \"電子音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit.info\": \"電子音の音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo\": \"バンジョー音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo.info\": \"バンジョーの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling\": \"電子ピアノ音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling.info\": \"電子ピアノの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.zombie\": \"ゾンビ音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.zombie.info\": \"ゾンビの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.skeleton\": \"スケルトン音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.skeleton.info\": \"スケルトンの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.creeper\": \"クリーパー音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.creeper.info\": \"クリーパーの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.dragon\": \"ドラゴン音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.dragon.info\": \"ドラゴンの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.wither_skeleton\": \"ウィザースケルトン音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.wither_skeleton.info\": \"ウィザースケルトンの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.piglin\": \"ピグリン音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.piglin.info\": \"ピグリンの音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.custom_head\": \"頭音\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.custom_head.info\": \"頭の音域を出力する。予想される範囲は0から24\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"サウンド\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"指定されたサウンド名を再生\",\n    \"aspect.integrateddynamics.write.string.audio.text\": \"テキスト\",\n    \"aspect.integrateddynamics.write.string.audio.text.info\": \"指定されたテキストを音声に変換\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager\": \"パーティクル: 村人の怒り\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble\": \"パーティクル: 泡\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble.info\": \"泡のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud\": \"パーティクル: もや\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud.info\": \"もやのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit\": \"パーティクル: クリティカル\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit.info\": \"クリティカルのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator\": \"パーティクル: ダメージ表示\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath\": \"パーティクル: ドラゴンブレス\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath.info\": \"ドラゴンブレスのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava\": \"パーティクル: 溶岩のしずく(ブロックから落ち始め)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava\": \"パーティクル: 溶岩のしずく(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava.info\": \"落ちている溶岩のしずくのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava\": \"パーティクル: 溶岩のしずく(ブロックに落ちた後)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water\": \"パーティクル: 水滴(ブロックから落ち始め)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water\": \"パーティクル: 水滴(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water.info\": \"落ちている水滴のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect\": \"パーティクル: エフェクト\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect.info\": \"エフェクトのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian\": \"パーティクル: エルダーガーディアン\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit\": \"パーティクル: エンチャントされた攻撃\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant\": \"パーティクル: エンチャント\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant.info\": \"エンチャントのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod\": \"パーティクル: エンドロッド\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod.info\": \"エンドロッドのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter\": \"パーティクル: 大爆発\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter.info\": \"大爆発のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion\": \"パーティクル: 爆発\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sonic_boom\": \"パーティクル: ソニックブーム\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework\": \"パーティクル: 花火\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework.info\": \"花火のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing\": \"パーティクル: 釣り\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame\": \"パーティクル: 炎\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame.info\": \"炎のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame\": \"パーティクル: 魂の炎\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame.info\": \"魂の炎のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul\": \"パーティクル: スカルクの魂\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul.info\": \"スカルクの魂のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop\": \"パーティクル: スカルクチャージポップ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop.info\": \"スカルクチャージポップのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul\": \"パーティクル: ソウル\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul.info\": \"ソウルのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash\": \"パーティクル: フラッシュ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash.info\": \"フラッシュのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager\": \"パーティクル: 村人の幸せ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter\": \"パーティクル: コンポスター\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter.info\": \"コンポスターのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart\": \"パーティクル: ハート\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart.info\": \"ハートのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect\": \"パーティクル: インスタントエフェクト\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect.info\": \"インスタントエフェクトのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime\": \"パーティクル: スライム\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime.info\": \"スライムのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball\": \"パーティクル: 雪玉\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball.info\": \"雪玉のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke\": \"パーティクル: 大きい煙\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke.info\": \"大きい煙のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava\": \"パーティクル: 溶岩\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava.info\": \"溶岩のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium\": \"パーティクル: 菌糸\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium.info\": \"菌糸のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note\": \"パーティクル: 音符\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note.info\": \"音符のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof\": \"パーティクル: 消滅\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof.info\": \"消滅のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal\": \"パーティクル: ポータル\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal.info\": \"ポータルのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain\": \"パーティクル: 雨\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain.info\": \"雨のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke\": \"パーティクル: 煙\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke.info\": \"煙のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze\": \"パーティクル: くしゃみ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze.info\": \"くしゃみのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit\": \"パーティクル: つば\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit.info\": \"つばのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink\": \"パーティクル: イカスミ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink.info\": \"イカスミのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack\": \"パーティクル: なぎ払い攻撃\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack.info\": \"なぎ払い攻撃のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying\": \"パーティクル: 不死のトーテム\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying.info\": \"不死のトーテムのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater\": \"パーティクル: 水中\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash\": \"パーティクル: 飛沫\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash.info\": \"飛沫のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch\": \"パーティクル: ウィッチ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch.info\": \"ウィッチのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop\": \"パーティクル: バブルポップ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop.info\": \"バブルポップのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down\": \"パーティクル: 下降気泡\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down.info\": \"下降気泡のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up\": \"パーティクル: 上昇気泡\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up.info\": \"上昇気泡のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus\": \"パーティクル: コンジット\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus.info\": \"コンジットのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin\": \"パーティクル: イルカ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin.info\": \"イルカのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke\": \"パーティクル: 焚き火の煙\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke.info\": \"焚き火の煙のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke\": \"パーティクル: 焚き火の狼煙\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke.info\": \"焚き火の狼煙のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey\": \"パーティクル: ハチミツ(ブロックから落ち始め)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey.info\": \"ブロックから落ちているハチミツのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey\": \"パーティクル: ハチミツ(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey.info\": \"落ちているハチミツのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey\": \"パーティクル: ハチミツ(ブロックに落ちた後)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey.info\": \"ブロックに落ちたハチミツのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar\": \"パーティクル: 花粉(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar.info\": \"落ちている花粉のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom\": \"パーティクル: 落ちる胞子の花\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom.info\": \"落ちる胞子の花のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash\": \"パーティクル: アッシュ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash.info\": \"アッシュのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore\": \"パーティクル: 真紅の胞子\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore.info\": \"真紅の胞子のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore\": \"パーティクル: 歪んだ胞子\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore.info\": \"歪んだ胞子のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air\": \"パーティクル: 胞子の花の空気\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air.info\": \"胞子の花の空気のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear\": \"パーティクル: 泣く黒曜石(ブロックから落ち始め)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear.info\": \"ブロックから落ちている泣く黒曜石のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear\": \"パーティクル: 泣く黒曜石(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear.info\": \"落ちている泣く黒曜石のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear\": \"パーティクル: 泣く黒曜石(ブロックに落ちた後)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear.info\": \"ブロックに落ちた泣く黒曜石のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal\": \"パーティクル: リバースポータル\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal.info\": \"リバースポータルのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash\": \"パーティクル: 白いアッシュ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash.info\": \"白いアッシュのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame\": \"パーティクル: 小さな炎\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame.info\": \"小さな炎のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake\": \"パーティクル: 雪の結晶\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake.info\": \"雪の結晶のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava\": \"パーティクル: 鍾乳石の溶岩(ブロックから落ち始め)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava.info\": \"鍾乳石から落ちている溶岩のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava\": \"パーティクル: 鍾乳石の溶岩(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava.info\": \"落ちている鍾乳石の溶岩のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water\": \"パーティクル: 鍾乳石の水(ブロックから落ち始め)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water.info\": \"鍾乳石から落ちている水のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water\": \"パーティクル: 鍾乳石の水(ブロックに落ちる途中)\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water.info\": \"落ちている鍾乳石の水のパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink\": \"パーティクル: 輝くイカスミ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink.info\": \"輝くイカスミのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow\": \"パーティクル: 輝き\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow.info\": \"輝きのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on\": \"パーティクル: ワックスON\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on.info\": \"ワックスONのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off\": \"パーティクル: ワックスOFF\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off.info\": \"ワックスOFFのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark\": \"パーティクル: 電気スパーク\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark.info\": \"電気スパークのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape\": \"パーティクル: スクレイプ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape.info\": \"スクレイプのパーティクルを一定の速度で放出します。\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"ブロックがあるか\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"対象空間にブロックが存在する場合\",\n    \"aspect.integrateddynamics.read.string.block.dimension\": \"ディメンション\",\n    \"aspect.integrateddynamics.read.string.block.dimension.info\": \"対象ワールドディメンションのID\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"X座標\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"対象のX座標\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Y座標\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"対象のY座標\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Z座標\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"対象のZ座標\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"タイルエンティティNBT\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"NBTとして対象のタイルエンティティ\",\n    \"aspect.integrateddynamics.read.string.block.biome\": \"バイオーム\",\n    \"aspect.integrateddynamics.read.string.block.biome.info\": \"対象のバイオーム名\",\n    \"aspect.integrateddynamics.read.integer.block.light\": \"明るさレベル\",\n    \"aspect.integrateddynamics.read.integer.block.light.info\": \"対象の明るさレベル\",\n    \"aspect.integrateddynamics.read.block.block\": \"ブロック\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"対象ブロック\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"アイテムフレームの回転\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"添付されたアイテムフレーム内のアイテムが持つ回転のインデックス。\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"エンティティ\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"対象空間内のエンティティ\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"プレイヤー\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"対象空間にいるプレイヤー\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"エンティティ\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"対象空間内の選択されたエンティティ\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"アイテムフレーム\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"リーダーに添付されたアイテムフレームのアイテム\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"ランダム\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"ランダムな整数値\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"プレイヤー数\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"サーバー内のプレイヤーの数\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"ティック時間\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"サーバーの平均ティック時間(ナノ秒)\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"全ワールドにおける1秒あたりのティック数の平均\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"プレイヤー\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"このサーバーでオンラインになっているプレイヤー\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"タンクが空\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"対象の流体タンクが空の場合\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"タンクが満杯\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"対象の流体タンクが満杯の場合\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"タンクが空ではない\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"対象の流体タンクに何かが入っている場合\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"タンクか\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"対象が流体タンクである場合\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"タンク\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"対象で発見したタンクの数\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"流体充填率\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"アクティブタンク内の流体の量をその容量で割った値\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"タンクの流体\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"全タンク内の流体のリスト\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"タンクの容量\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"全タンクの容量のリスト\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"タンクの流体\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"対象タンク内の流体\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"ブロックの流体\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"対象ブロック内の流体\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"インベントリが満杯\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"すべてのスロットに少なくとも1つのアイテムがある場合\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"インベントリが空\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"すべてのスロットが空の場合\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"インベントリが空でない\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"少なくとも1つのアイテムがある場合\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"インベントリか\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"対象がインベントリである場合\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"インベントリのカウント\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"アイテムの合計数を取得\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"スロットスタックサイズ\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"アクティブスロットのスタックサイズを取得\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"スロット\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"インベントリ内のスロット数\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"埋まったスロット数\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"アイテムがあるスロットの数\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"充填率\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"空でないスロットの数をスロットの合計数で割った数\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"アイテム数\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"インベントリ内のアイテムのリストを取得\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"スロットアイテム\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"アクティブスロットのアイテムを取得\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"ワーカーか\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"対象がワーカーマシンの場合\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"作業があるか\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"対象に処理すべき作業がある場合\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"作業可能\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"対象が現在の状態で作業を開始できる場合\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"作業中か\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"対象が現在作業中の場合\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"温度があるか\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"対象が一定の温度を持っている場合\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"温度\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature.info\": \"対象の現在の温度(ケルビン)\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"最高温度\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature.info\": \"対象の最高温度(ケルビン)\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"最低温度\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature.info\": \"対象の最低温度(ケルビン)\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"デフォルトの温度\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature.info\": \"対象のデフォルトの温度(ケルビン)\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"レシピを扱えるか\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"対象がレシピを扱える場合\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"レシピ\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"対象が扱えるレシピ\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"入力によるレシピ出力\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"指定された入力に対する結果のレシピ出力を返す演算子です。\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"出力によるレシピ入力\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"指定された出力に対して可能なすべてのレシピ入力を返す演算子です。\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"入力によるレシピ\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"指定された入力に対して可能なすべてのレシピを返す演算子です。\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"出力によるレシピ\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"指定された出力に対して可能なすべてのレシピを返す演算子です。\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"入力によるレシピ\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"指定された入力に対するレシピを返す演算子です。\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"出力によるレシピ\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"指定された出力のレシピを返す演算子です。\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"ネットワークか\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"対象がネットワークである場合\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"要素\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"対象ネットワーク内の要素数\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"エネルギーバッテリー\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"対象ネットワーク内のエネルギーバッテリーの量\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"エネルギー消費率\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"対象ネットワーク内のすべてのIDパーツの現在のエネルギー消費率(ティックあたり)\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"値\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"対象によって公開されている値\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"レッドストーン高\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"レッドストーンのレベルが最大の場合\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"レッドストーン低\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"レッドストーンレベルがない場合\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"レッドストーン\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"レッドストーンレベルがある場合\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"レッドストーンクロック\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"長さと間隔を設定できる信号です。\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"レッドストーン\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"正確なレッドストーンレベルを取得する\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"コンパレーター\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"コンパレーターの値を取得する\",\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"レッドストーン\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"trueの場合、完全なレッドストーン信号を出力\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"レッドストーン信号\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"指定された値が変更された場合、設定されたレッドストーン値で信号を出力します。\",\n    \"aspect.integrateddynamics.write.integer.redstone\": \"レッドストーン\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"正確なレッドストーン信号として値を出力\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"レッドストーン信号\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"指定された値が変更された場合、設定されたレッドストーン値で信号を出力します。\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"エンティティがいるか\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"対象空間にエンティティが存在する場合\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"Mobがいるか\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"対象空間にMobが存在する場合\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"動物がいるか\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"対象空間に動物が存在する場合\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"プレイヤーがいるか\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"対象空間にプレイヤーが存在する場合\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"アイテムがあるか\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"対象空間にアイテムが存在する場合\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"天気: 晴れ\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"ワールドが晴れである場合\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"天気: 雨\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining.info\": \"ワールドが雨である場合\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"天気: 雷雨\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"ワールドが雷雨である場合\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"日中か\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"対象ワールドが昼である場合\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"夜間か\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"対象ワールドが夜である場合\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"エンティティ\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"対象空間内のエンティティ数\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"雨のカウントダウン\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"このワールドに雨が降るまでのティック数\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"ティック時間\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"このワールドの平均ティック時間(ミリ秒)\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"時刻\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"対象ワールド内で経過した時間\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"明るさレベル\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"対象の明るさレベル\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"プレイヤー数\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"このワールドでオンラインになっているプレイヤー\",\n    \"aspect.integrateddynamics.read.double.world.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"このワールドのティック/秒\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"時間\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"時刻設定コマンドに依存するワールドの時間\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"合計時間\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"時刻設定コマンドに依存しないワールドの時間\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"ワールド名\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"対象のワールド名\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"プレイヤー\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"このワールドにいるプレイヤー\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"タンクID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"スロットID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"リスト要素\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"範囲\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"チャンネル\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"信号間隔\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"信号強度\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"信号時間オフセット\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"ボリューム\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"周波数\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"X軸補正\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Y軸補正\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Z軸補正\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"パーティクル数\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"X軸の広さ\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Y軸の広さ\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Z軸の広さ\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"強い力\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"通常のレッドストーンダストの代わりにレッドストーントーチをシミュレートする必要がある場合\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"信号値\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"発信する信号の値です。\",\n    \"operator.integrateddynamics\": \"演算子\",\n    \"operator.integrateddynamics.applied.operator_name\": \"適用済みの%s [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"§e適用済み: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§e演算子: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§eカテゴリ: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§e変数ID: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§e入力型 %s: §r\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§e出力型: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"演算子%sは長さ%sの入力を受け取りましたが、必要なのは長さ%sです。\",\n    \"operator.integrateddynamics.error.operator_param_wrong_input_length\": \"演算子の入力長は%sである必要がありますが、%sの入力長は%sのみです。\",\n    \"operator.integrateddynamics.error.null_type\": \"演算子%sは、位置%sにnull変数を含む入力を受け取りました。\",\n    \"operator.integrateddynamics.error.wrong_type\": \"演算子%1$sは、位置%3$sで型%2$sの入力を受け取りましたが、型%4$sが期待されていました。\",\n    \"operator.integrateddynamics.error.wrong_type_output\": \"演算子%1$sの評価では、予想された%3$sではなく%2$sが返されました。\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"演算子%1$sは位置%3$sで型%2$sの入力を受け取りましたが、カリー化には型%4$sが期待されていました。\",\n    \"operator.integrateddynamics.error.currying_overflow\": \"演算子%s(%s入力)を%s入力でカリー化しているときに、出力として演算子が期待されましたが、%sが返されました。\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"演算子%1$sは、型%3$sが期待されていたのに、型%2$sを出力する入力演算子を受け取りました。\",\n    \"operator.integrateddynamics.error.illegal_property\": \"プロパティは%1$sを出力すると予想されますが、演算子%3$sでは%2$sが見つかりました。\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"ID%sの変数が現在のネットワーク内に見つかりませんでした。\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"ID%sの変数はそれ自体に依存しており、無限再帰です。\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"演算子%1$sは、長さ%4$sが必要なのに、入力長%3$sの演算子%2$sを受け取りました。\",\n    \"operator.integrateddynamics.error.operator_recursion_limit\": \"%2$sの%1$s呼び出しの演算子再帰制限に達しました。ゲームをクラッシュさせようとしているのですか？\",\n    \"operator.integrateddynamics.error.operator_nbt_path_expression\": \"無効なNBTパス式「%s」が見つかりました: %s\",\n    \"operator.integrateddynamics.error.cast.no_mapping\": \"%sから%sへのキャストマッピングが存在しません。\",\n    \"operator.integrateddynamics.error.cast.illegal\": \"値「%3$s」に対して、%1$sを%2$sにキャストしようとしました。\",\n    \"operator.integrateddynamics.error.cast.unexpected\": \"%1$s型の値は、%3$sにキャストする予期される%2$s型に対応していません。\",\n    \"operator.integrateddynamics.error.divide_by_zero\": \"ゼロによる除算は許可されません。\",\n    \"operator.integrateddynamics.error.parse\": \"値「%s」を%sに解析できませんでした。\",\n    \"operator.integrateddynamics.error.no_derserializer\": \"演算子値「%s」 をデシリアライズするためのシリアライザーが見つかりませんでした。\",\n    \"operator.integrateddynamics.error.regex.invalid\": \"無効な正規表現: 「%s」\",\n    \"operator.integrateddynamics.error.substring.index_negative\": \"部分文字列のインデックスは負であってはなりません。\",\n    \"operator.integrateddynamics.error.substring.longer_than_string\": \"部分文字列のインデックスは文字列の長さを超えてはなりません。\",\n    \"operator.integrateddynamics.error.group.index_negative\": \"グループインデックスは負であってはなりません。\",\n    \"operator.integrateddynamics.error.group.no_match\": \"「%2$s」内の正規表現「%1$s」に一致するグループ正規表現が見つかりませんでした。\",\n    \"operator.integrateddynamics.error.group.no_match_group\": \"グループ「%3$s」の「%2$s」内の正規表現「%1$s」に一致するグループ正規表現が見つかりませんでした。\",\n    \"operator.integrateddynamics.error.regex_scan.index_negative\": \"正規表現スキャンインデックスは負であってはなりません。\",\n    \"operator.integrateddynamics.error.regex_scan.no_match_group\": \"グループ「%3$s」の「%2$s」内の正規表現「%1$s」に一致する正規表現スキャンは見つかりませんでした。\",\n    \"operator.integrateddynamics.error.replace_regex.invalid_group\": \"正規表現置換「%s」に無効なグループがあります: %s\",\n    \"operator.integrateddynamics.error.infinite_list.illegal\": \"演算子%sは無限リストには適用できません。\",\n    \"operator.integrateddynamics.error.index_out_of_bounds\": \"インデックスが範囲外です。長さ%2$sのリストの要素%1$sを取得しようとしました。インデックスが範囲外の場合は、getOrDefault演算子を使用してデフォルトを指定します。\",\n    \"operator.integrateddynamics.error.slice.index_negative\": \"スライスインデックスは負の値であってはなりません。\",\n    \"operator.integrateddynamics.error.reduce.empty\": \"reduce1演算子は空のリストの先頭を取得しようとしました。代わりに、空のリストをサポートするために、reduce演算子を使用して基本値を指定してください。\",\n    \"operator.integrateddynamics.error.operator_not_found\": \"名前が「%s」の演算子が見つかりませんでした。\",\n    \"operator.integrateddynamics.logical\": \"論理式\",\n    \"operator.integrateddynamics.logical.basename\": \"論理式 %s\",\n    \"operator.integrateddynamics.logical.and\": \"AND\",\n    \"operator.integrateddynamics.logical.or\": \"OR\",\n    \"operator.integrateddynamics.logical.not\": \"NOT\",\n    \"operator.integrateddynamics.logical.nand\": \"NAND\",\n    \"operator.integrateddynamics.logical.nor\": \"NOR\",\n    \"operator.integrateddynamics.arithmetic\": \"算術\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"算術 %s\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"加算\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"減算\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"乗算\",\n    \"operator.integrateddynamics.arithmetic.division\": \"除算\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"最大\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"2つの値の中で最も大きい値です。\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"最小\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"2つの値の中で最も小さい値です。\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"係数\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"増分\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"減分\",\n    \"operator.integrateddynamics.integer\": \"整数\",\n    \"operator.integrateddynamics.integer.basename\": \"整数 %s\",\n    \"operator.integrateddynamics.integer.modulus\": \"係数\",\n    \"operator.integrateddynamics.integer.increment\": \"増分\",\n    \"operator.integrateddynamics.integer.decrement\": \"減分\",\n    \"operator.integrateddynamics.relational\": \"関係式\",\n    \"operator.integrateddynamics.relational.basename\": \"関係式 %s\",\n    \"operator.integrateddynamics.relational.equals\": \"=\",\n    \"operator.integrateddynamics.relational.gt\": \"＞\",\n    \"operator.integrateddynamics.relational.lt\": \"＜\",\n    \"operator.integrateddynamics.relational.notequals\": \"≠\",\n    \"operator.integrateddynamics.relational.ge\": \"≧\",\n    \"operator.integrateddynamics.relational.le\": \"≦\",\n    \"operator.integrateddynamics.binary\": \"バイナリ\",\n    \"operator.integrateddynamics.binary.basename\": \"バイナリ %s\",\n    \"operator.integrateddynamics.binary.and\": \"AND\",\n    \"operator.integrateddynamics.binary.or\": \"OR\",\n    \"operator.integrateddynamics.binary.xor\": \"XOR\",\n    \"operator.integrateddynamics.binary.complement\": \"補完\",\n    \"operator.integrateddynamics.binary.rshift\": \"右シフト\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"右方向シフト(1で埋める)\",\n    \"operator.integrateddynamics.binary.lshift\": \"左シフト\",\n    \"operator.integrateddynamics.binary.rzshift\": \"右ゼロシフト\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"右方向シフト(0で埋める)\",\n    \"operator.integrateddynamics.string\": \"文字列\",\n    \"operator.integrateddynamics.string.basename\": \"文字列 %s\",\n    \"operator.integrateddynamics.string.length\": \"長さ\",\n    \"operator.integrateddynamics.string.length.info\": \"指定した文字列の長さ\",\n    \"operator.integrateddynamics.string.concat\": \"Concat\",\n    \"operator.integrateddynamics.string.concat.info\": \"指定した2つの文字列を1つに連結\",\n    \"operator.integrateddynamics.string.contains\": \"文字列を含む\",\n    \"operator.integrateddynamics.string.contains.info\": \"指定した部分文字列が指定した文字列の中に含まれている場合。\",\n    \"operator.integrateddynamics.string.contains_regex\": \"正規表現を含む\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"指定された正規表現が、指定された文字列の任意の部分文字列と一致する場合。\",\n    \"operator.integrateddynamics.string.matches_regex\": \"正規表現に一致\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"指定された正規表現が指定された文字列と一致する場合。\",\n    \"operator.integrateddynamics.string.index_of\": \"Index Of\",\n    \"operator.integrateddynamics.string.index_of.info\": \"指定された文字列内の検索語に一致する部分文字列の最初の出現のインデックスを取得します。\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"正規表現のインデックス\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"指定された文字列内のパターンに一致する部分文字列の最初の出現のインデックスを取得します。\",\n    \"operator.integrateddynamics.string.starts_with\": \"以下で始まる\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"指定された部分文字列が指定された文字列の先頭と一致する場合。\",\n    \"operator.integrateddynamics.string.ends_with\": \"以下で終わる\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"指定された文字列が指定された文字列の末尾と一致する場合。\",\n    \"operator.integrateddynamics.string.split_on\": \"Split On\",\n    \"operator.integrateddynamics.string.split_on.info\": \"指定された文字列を指定された (リテラル) 区切り文字で分割した部分を含むリストを取得します。\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"正規表現で分割\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"指定された正規表現で分割された、指定された文字列の部分を含むリストを取得します。\",\n    \"operator.integrateddynamics.string.substring\": \"部分文字列\",\n    \"operator.integrateddynamics.string.substring.info\": \"指定された文字列の、指定されたインデックス(含む)から指定されたインデックス(含まない)までの部分文字列を取得します。\",\n    \"operator.integrateddynamics.string.regex_group\": \"正規表現グループ\",\n    \"operator.integrateddynamics.string.regex_group.info\": \"指定された正規表現を検索し、指定された文字列内の指定されたインデックスのグループに一致するものを返します。\",\n    \"operator.integrateddynamics.string.regex_groups\": \"正規表現グループ\",\n    \"operator.integrateddynamics.string.regex_groups.info\": \"指定された正規表現を検索し、指定された文字列内で一致するすべてのグループのリストを返します。\",\n    \"operator.integrateddynamics.string.regex_scan\": \"正規表現スキャン\",\n    \"operator.integrateddynamics.string.regex_scan.info\": \"指定された正規表現に一致するすべての部分を検索し、指定された文字列内の指定されたインデックスにあるグループを返します。\",\n    \"operator.integrateddynamics.string.replace\": \"置換\",\n    \"operator.integrateddynamics.string.replace.info\": \"指定された文字列内で、検索用語のすべてのインスタンスを検索し、指定された文字列に置き換えます。\",\n    \"operator.integrateddynamics.string.replace_regex\": \"正規表現の置換\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"検索パターンに一致するすべての部分を検索し、指定された文字列で置き換えます。\",\n    \"operator.integrateddynamics.string.join\": \"Join\",\n    \"operator.integrateddynamics.string.join.info\": \"指定された文字列区切り文字と指定された文字列リストを使用して結合された文字列を作成します。\",\n    \"operator.integrateddynamics.string.name\": \"To String\",\n    \"operator.integrateddynamics.string.name.info\": \"名前付きオブジェクトの文字列または名前を取得するか、文字列に変換\",\n    \"operator.integrateddynamics.string.unique_name\": \"固有の名前\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"オブジェクトの一意の名前を取得\",\n    \"operator.integrateddynamics.double\": \"ダブル\",\n    \"operator.integrateddynamics.double.basename\": \"ダブル %s\",\n    \"operator.integrateddynamics.double.sqrt\": \"平方根\",\n    \"operator.integrateddynamics.double.pow\": \"電力\",\n    \"operator.integrateddynamics.number\": \"数値\",\n    \"operator.integrateddynamics.number.basename\": \"数値 %s\",\n    \"operator.integrateddynamics.number.round\": \"Round\",\n    \"operator.integrateddynamics.number.round.info\": \"最も近い整数に丸める\",\n    \"operator.integrateddynamics.number.ceil\": \"Ceil\",\n    \"operator.integrateddynamics.number.ceil.info\": \"最も近い整数に切り上げ\",\n    \"operator.integrateddynamics.number.floor\": \"Floor\",\n    \"operator.integrateddynamics.number.floor.info\": \"最も近い整数に切り捨て\",\n    \"operator.integrateddynamics.number.compact\": \"Compact\",\n    \"operator.integrateddynamics.number.compact.info\": \"数値のより短く、より正確でない表現を表示\",\n    \"operator.integrateddynamics.list\": \"リスト\",\n    \"operator.integrateddynamics.list.basename\": \"リスト %s\",\n    \"operator.integrateddynamics.list.length\": \"長さ\",\n    \"operator.integrateddynamics.list.length.info\": \"指定したリストの長さ\",\n    \"operator.integrateddynamics.list.empty\": \"空\",\n    \"operator.integrateddynamics.list.empty.info\": \"リストが空の場合\",\n    \"operator.integrateddynamics.list.notempty\": \"空でない\",\n    \"operator.integrateddynamics.list.notempty.info\": \"リストが空でない場合\",\n    \"operator.integrateddynamics.list.get\": \"Get\",\n    \"operator.integrateddynamics.list.get.info\": \"指定された位置にあるリスト要素を取得します。インデックスが範囲外の場合はエラーがスローされます。\",\n    \"operator.integrateddynamics.list.get_or_default\": \"Get Or Default\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"指定された位置にあるリスト要素を安全に取得し、その要素が利用できない場合は、指定されたデフォルト値を返します。\",\n    \"operator.integrateddynamics.list.contains\": \"Contains\",\n    \"operator.integrateddynamics.list.contains.info\": \"リストに指定された要素が含まれている場合。\",\n    \"operator.integrateddynamics.list.contains_p\": \"述語を含む\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"リストの要素の少なくとも1つが指定された述語に対してtrueを返す場合。\",\n    \"operator.integrateddynamics.list.count\": \"カウント\",\n    \"operator.integrateddynamics.list.count.info\": \"指定された要素がリスト内で見つかった回数。\",\n    \"operator.integrateddynamics.list.count_p\": \"述語回数\",\n    \"operator.integrateddynamics.list.count_p.info\": \"リスト内の要素に対して、指定された述語がtrueを返す回数。\",\n    \"operator.integrateddynamics.list.append\": \"追加\",\n    \"operator.integrateddynamics.list.append.info\": \"指定されたリストに指定されたアイテムを追加します。\",\n    \"operator.integrateddynamics.list.concat\": \"Concat\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"遅延リストビルダー\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"開始値と前の要素に適用されて次の要素を取得する演算子を使用して、リストを遅延的に構築します。\",\n    \"operator.integrateddynamics.list.head\": \"ヘッド\",\n    \"operator.integrateddynamics.list.head.info\": \"指定されたリストの最初の要素を取得します。\",\n    \"operator.integrateddynamics.list.tail\": \"テイル\",\n    \"operator.integrateddynamics.list.tail.info\": \"リストから最初の要素を削除します。\",\n    \"operator.integrateddynamics.list.uniq_p\": \"一意の述語\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"指定された比較述語に基づいて、リスト内のすべての要素を重複排除します。\",\n    \"operator.integrateddynamics.list.uniq\": \"ユニーク\",\n    \"operator.integrateddynamics.list.uniq.info\": \"リスト内のすべての要素を重複排除します。\",\n    \"operator.integrateddynamics.list.slice\": \"スライス\",\n    \"operator.integrateddynamics.list.slice.info\": \"指定されたインデックス(含む)から指定されたインデックス(含まない)までの指定されたリストのサブセットを取得します。\",\n    \"operator.integrateddynamics.list.intersection\": \"交差\",\n    \"operator.integrateddynamics.list.intersection.info\": \"指定された両方のリストに存在するアイテムのリストを返却\",\n    \"operator.integrateddynamics.block\": \"ブロック\",\n    \"operator.integrateddynamics.block.basename\": \"ブロック %s\",\n    \"operator.integrateddynamics.block.opaque\": \"不透過\",\n    \"operator.integrateddynamics.block.opaque.info\": \"指定されたブロックが不透過な場合、透明ではありません\",\n    \"operator.integrateddynamics.block.itemstack\": \"アイテム\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"指定されたブロックのアイテム表現\",\n    \"operator.integrateddynamics.block.mod\": \"Mod\",\n    \"operator.integrateddynamics.block.mod.info\": \"指定されたブロックを所有するModの名前\",\n    \"operator.integrateddynamics.block.breaksound\": \"ブロック破壊音\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"指定されたブロックの破壊音\",\n    \"operator.integrateddynamics.block.placesound\": \"ブロック設置音\",\n    \"operator.integrateddynamics.block.placesound.info\": \"指定されたブロックの配置音\",\n    \"operator.integrateddynamics.block.stepsound\": \"ブロックの足音\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"指定されたブロックの足音\",\n    \"operator.integrateddynamics.block.isshearable\": \"ブロックが剪断可能か\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"指定されたブロックが剪断可能な場合\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"ブロックがブロック植物を維持できるか\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"指定されたブロックが指定されたブロック植物を維持できる場合\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"ブロックがアイテム植物を維持できるか\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"指定されたブロックが指定されたアイテム植物を維持できる場合\",\n    \"operator.integrateddynamics.block.plantage\": \"ブロック植物の年齢\",\n    \"operator.integrateddynamics.block.plantage.info\": \"指定された植物ブロックの年齢\",\n    \"operator.integrateddynamics.block.blockbyname\": \"名前によるブロック\",\n    \"operator.integrateddynamics.block.blockproperties\": \"ブロックのプロパティ\",\n    \"operator.integrateddynamics.block.blockproperties.info\": \"ブロックのプロパティをNBT複合タグとして取得します。\",\n    \"operator.integrateddynamics.block.blockfromproperties\": \"ブロックのプロパティ\",\n    \"operator.integrateddynamics.block.blockfromproperties.info\": \"指定されたプロパティを適用した指定されたブロックを取得します。\",\n    \"operator.integrateddynamics.block.blockpossibleproperties\": \"ブロックのプロパティ\",\n    \"operator.integrateddynamics.block.tag\": \"ブロックタグ名\",\n    \"operator.integrateddynamics.block.tag.info\": \"指定されたブロックのタグ名(文字列)\",\n    \"operator.integrateddynamics.string.blocktag\": \"ブロックタグ値\",\n    \"operator.integrateddynamics.string.blocktag.info\": \"指定されたブロックタグ名のタグ値(ブロック)\",\n    \"operator.integrateddynamics.itemstack\": \"アイテム\",\n    \"operator.integrateddynamics.itemstack.basename\": \"アイテム %s\",\n    \"operator.integrateddynamics.itemstack.size\": \"サイズ\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"現在のスタックサイズ\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"最大サイズ\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"最大スタックサイズ\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"スタック可能\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"アイテムスタックが少なくとも2つのアイテムを保持できる場合。\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"損傷しやすい\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"アイテムがダメージを受ける可能性がある場合。\",\n    \"operator.integrateddynamics.itemstack.damage\": \"ダメージ\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"現在のアイテムのダメージ\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"最大ダメージ\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"最大アイテムダメージ\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"エンチャント済み\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"アイテムがエンチャント済みの場合\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"エンチャント可能\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"アイテムがエンチャント可能な場合\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"修復コスト\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"アイテムの修復コスト\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"レア度\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"アイテムのレア度\",\n    \"operator.integrateddynamics.itemstack.strength\": \"強度対ブロック\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"このアイテムが指定されたブロックに対して持つ強度\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"収穫できるか\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"アイテムが特定のブロックを収穫できる場合\",\n    \"operator.integrateddynamics.itemstack.block\": \"ブロック\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"指定したアイテムからのブロック\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"流体があるか\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"指定されたアイテムに流体が含まれている場合\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"流体\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"指定したアイテムからの流体\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"流体容量\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"指定されたアイテムの流体容量(mB)\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"NBTが等しい\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"指定されたアイテムのデータコンポーネント要素が等しい場合\",\n    \"operator.integrateddynamics.itemstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"指定されたアイテムを所有しているModの名前\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"燃焼時間\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"指定したアイテムのティック内の燃焼時間\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"燃料か\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"与えられたアイテムが燃料として使用できる場合\",\n    \"operator.integrateddynamics.itemstack.tag.info\": \"指定されたアイテムのタグ名(文字列)\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"アイテムがインベントリを持つか\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"指定されたアイテムがインベントリを持つ場合\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"アイテムインベントリサイズ\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"指定されたアイテムハンドラのコンテンツのインベントリサイズを取得\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"アイテムインベントリ\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"指定されたアイテムハンドラーの内容のインベントリを取得\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"スタックサイズ付きアイテム\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"指定されたアイテムのスタックサイズを設定\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"名前によるアイテム\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"アイテムリストカウント\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"リスト内の指定されたアイテムの合計アイテム数を取得します。\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"アイテムNBT\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"指定されたアイテムのデータをNBTとして取得します。\",\n    \"operator.integrateddynamics.itemstack.hasnbt\": \"NBTをもつか\",\n    \"operator.integrateddynamics.itemstack.hasnbt.info\": \"アイテムスタックにデータコンポーネントがある場合。\",\n    \"operator.integrateddynamics.itemstack.datakeys\": \"データキー\",\n    \"operator.integrateddynamics.itemstack.datakeys.info\": \"アイテムスタックのすべてのデータコンポーネントキーを取得します。\",\n    \"operator.integrateddynamics.itemstack.datavalue\": \"データ値\",\n    \"operator.integrateddynamics.itemstack.datavalue.info\": \"指定されたデータコンポーネントキーの値を取得します。\",\n    \"operator.integrateddynamics.itemstack.withdata\": \"データ付き\",\n    \"operator.integrateddynamics.itemstack.withdata.info\": \"指定されたデータコンポーネントのキーと値を指定されたアイテムスタックに追加します。\",\n    \"operator.integrateddynamics.entity\": \"エンティティ\",\n    \"operator.integrateddynamics.entity.basename\": \"エンティティ %s\",\n    \"operator.integrateddynamics.entity.ismob\": \"Mobか\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"エンティティがMobの場合\",\n    \"operator.integrateddynamics.entity.isanimal\": \"動物か\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"エンティティが動物の場合\",\n    \"operator.integrateddynamics.entity.isitem\": \"アイテムか\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"エンティティがアイテムの場合\",\n    \"operator.integrateddynamics.entity.isplayer\": \"プレイヤーか\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"エンティティがプレイヤーの場合\",\n    \"operator.integrateddynamics.entity.isminecart\": \"トロッコか\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"エンティティがトロッコの場合\",\n    \"operator.integrateddynamics.entity.item\": \"アイテム\",\n    \"operator.integrateddynamics.entity.item.info\": \"アイテムエンティティからのアイテム\",\n    \"operator.integrateddynamics.entity.health\": \"体力\",\n    \"operator.integrateddynamics.entity.health.info\": \"エンティティの体力\",\n    \"operator.integrateddynamics.entity.width\": \"幅\",\n    \"operator.integrateddynamics.entity.width.info\": \"エンティティの幅\",\n    \"operator.integrateddynamics.entity.height\": \"高さ\",\n    \"operator.integrateddynamics.entity.height.info\": \"エンティティの高さ\",\n    \"operator.integrateddynamics.entity.isburning\": \"燃えているか\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"エンティティが燃えている場合\",\n    \"operator.integrateddynamics.entity.iswet\": \"濡れているか\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"エンティティが濡れている場合\",\n    \"operator.integrateddynamics.entity.iscrouching\": \"しゃがんでいるか\",\n    \"operator.integrateddynamics.entity.iscrouching.info\": \"エンティティがしゃがんでいる場合\",\n    \"operator.integrateddynamics.entity.iseating\": \"食事中か\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"エンティティが食事中の場合\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"防具インベントリ\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"エンティティが防具として所持しているアイテムのリストです。\",\n    \"operator.integrateddynamics.entity.inventory\": \"プレイヤーインベントリ\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"プレイヤーが所持しているアイテムのリストです。\",\n    \"operator.integrateddynamics.entity.mod\": \"Mod\",\n    \"operator.integrateddynamics.entity.mod.info\": \"指定されたエンティティを所有しているModの名前\",\n    \"operator.integrateddynamics.entity.targetblock\": \"対象ブロック\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"指定されたエンティティが現在見ているブロック。\",\n    \"operator.integrateddynamics.entity.targetentity\": \"対象エンティティ\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"指定されたエンティティが現在見ているエンティティ。\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"GUIを開いているか\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"指定されたプレーヤーが外部GUIを開いている場合。\",\n    \"operator.integrateddynamics.entity.helditem\": \"メインハンドに持っているアイテム\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"指定されたエンティティが現在メインハンドに持っているアイテム。\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"オフハンドに持っているアイテム\",\n    \"operator.integrateddynamics.entity.mounted\": \"マウントされているエンティティ\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"指定されたエンティティに現在マウントされているエンティティ。\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"アイテムフレームの内容\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"指定されたアイテムフレームの内容。\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"アイテムフレームの回転\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"指定されたアイテムフレームの回転。\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"エンティティの傷音\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"指定されたエンティティの傷音。\",\n    \"operator.integrateddynamics.entity.deathsound\": \"エンティティの死亡音\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"指定されたエンティティの死亡音。\",\n    \"operator.integrateddynamics.entity.age\": \"エンティティの年齢\",\n    \"operator.integrateddynamics.entity.age.info\": \"指定されたエンティティの年齢。\",\n    \"operator.integrateddynamics.entity.ischild\": \"エンティティが子供か\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"指定されたエンティティが子供である場合。\",\n    \"operator.integrateddynamics.entity.canbreed\": \"エンティティは繁殖可能か\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"指定されたエンティティが繁殖する準備ができている場合。\",\n    \"operator.integrateddynamics.entity.isinlove\": \"エンティティが求愛状態\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"指定されたエンティティが求愛状態で繁殖準備ができている場合。\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"エンティティはアイテムで繁殖可能か\",\n    \"operator.integrateddynamics.entity.isshearable\": \"エンティティが剪断可能か\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"指定されたエンティティが剪断可能な場合\",\n    \"operator.integrateddynamics.entity.nbt\": \"エンティティNBT\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"指定されたエンティティをNBTとして取得します。\",\n    \"operator.integrateddynamics.entity.entitytype\": \"エンティティタイプ\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"エンティティタイプ名。\",\n    \"operator.integrateddynamics.entity.entityitems\": \"エンティティアイテム\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"このエンティティ内に含まれるアイテム。\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"エンティティ流体\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"このエンティティ内に含まれる流体。\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"エンティティのエネルギー貯蔵量\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"このエンティティに保存されているエネルギーの量。\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"エンティティのエネルギー容量\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"このエンティティのエネルギー容量。\",\n    \"operator.integrateddynamics.entity.entityitemtooltip\": \"エンティティツールチップ\",\n    \"operator.integrateddynamics.entity.entityitemtooltip.info\": \"指定されたプレイヤーエンティティのコンテキストで、指定されたアイテムのツールチップを文字列のリストとして取得します。\",\n    \"operator.integrateddynamics.itemstack.tooltip\": \"ツールチップ\",\n    \"operator.integrateddynamics.itemstack.tooltip.info\": \"指定されたアイテムのツールチップを文字列のリストとして取得します。\",\n    \"operator.integrateddynamics.fluidstack\": \"流体\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"流体 %s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"量\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"mB量\",\n    \"operator.integrateddynamics.fluidstack.block\": \"ブロック\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"流体のブロック\",\n    \"operator.integrateddynamics.fluidstack.light_level\": \"明るさレベル\",\n    \"operator.integrateddynamics.fluidstack.light_level.info\": \"流体によって放出される明るさレベル\",\n    \"operator.integrateddynamics.fluidstack.density\": \"密度\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"流体密度\",\n    \"operator.integrateddynamics.fluidstack.temperature\": \"温度\",\n    \"operator.integrateddynamics.fluidstack.temperature.info\": \"流体温度\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"粘度\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"流体粘度\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air\": \"空気より軽いか\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air.info\": \"流体が空気より軽い場合\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"レア度\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"流体レア度\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize\": \"流体蒸発音\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize.info\": \"流体の蒸発音\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty\": \"バケツを空にする音\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty.info\": \"流体のバケツを空にする音\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill\": \"バケツに入れる音\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill.info\": \"流体のバケツに入れる音\",\n    \"operator.integrateddynamics.fluidstack.bucket\": \"バケツ\",\n    \"operator.integrateddynamics.fluidstack.bucket.info\": \"流体用の満たされたバケツ\",\n    \"operator.integrateddynamics.fluidstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"流体NBT\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"指定された流体のデータをNBTとして取得します。\",\n    \"operator.integrateddynamics.fluidstack.datakeys\": \"データキー\",\n    \"operator.integrateddynamics.fluidstack.datakeys.info\": \"流体スタックのすべてのデータコンポーネントキーを取得します。\",\n    \"operator.integrateddynamics.fluidstack.datavalue\": \"データ値\",\n    \"operator.integrateddynamics.fluidstack.datavalue.info\": \"指定されたデータコンポーネントキーの値を取得します。\",\n    \"operator.integrateddynamics.fluidstack.withdata\": \"データ付き\",\n    \"operator.integrateddynamics.fluidstack.withdata.info\": \"指定されたデータコンポーネントのキーと値を指定された流体スタックに追加します。\",\n    \"operator.integrateddynamics.fluidstack.tag\": \"流体タグ名\",\n    \"operator.integrateddynamics.string.fluidtag\": \"流体タグ値\",\n    \"operator.integrateddynamics.nbt\": \"NBT\",\n    \"operator.integrateddynamics.nbt.basename\": \"NBT %s\",\n    \"operator.integrateddynamics.nbt.compound_size\": \"NBT複合サイズ\",\n    \"operator.integrateddynamics.nbt.compound_size.info\": \"指定されたNBT複合タグ内のエントリの数\",\n    \"operator.integrateddynamics.operator\": \"演算子\",\n    \"operator.integrateddynamics.operator.basename\": \"演算子 %s\",\n    \"operator.integrateddynamics.operator.apply\": \"適用\",\n    \"operator.integrateddynamics.operator.apply.info\": \"指定された演算子に指定した値を適用します。\",\n    \"operator.integrateddynamics.operator.apply2\": \"2を適用\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"指定された2つの値を演算子に適用します。\",\n    \"operator.integrateddynamics.operator.apply3\": \"3を適用\",\n    \"operator.integrateddynamics.operator.apply_n\": \"Nを適用\",\n    \"operator.integrateddynamics.operator.apply_n.info\": \"指定された演算子に値のリストを適用します。\",\n    \"operator.integrateddynamics.operator.apply0\": \"0を適用\",\n    \"operator.integrateddynamics.operator.apply0.info\": \"引数なしで指定した演算子に適用します。\",\n    \"operator.integrateddynamics.operator.map\": \"マップ\",\n    \"operator.integrateddynamics.operator.map.info\": \"指定された演算子をリストのすべての要素に適用し、マップされた値の新しいリストを生成します。\",\n    \"operator.integrateddynamics.operator.filter\": \"フィルター\",\n    \"operator.integrateddynamics.operator.filter.info\": \"指定された述語とすべての要素を一致させることにより、要素のリストをフィルタリングします。\",\n    \"operator.integrateddynamics.operator.conjunction\": \"結合\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"2つの述語の結合をとります。\",\n    \"operator.integrateddynamics.operator.disjunction\": \"論理和\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"2つの述語の論理和をとります。\",\n    \"operator.integrateddynamics.operator.negation\": \"否定\",\n    \"operator.integrateddynamics.operator.negation.info\": \"述語の否定をとります。\",\n    \"operator.integrateddynamics.virtual.recipeoutputbyinput\": \"入力によるレシピ出力\",\n    \"operator.integrateddynamics.virtual.recipeinputsbyoutput\": \"出力によるレシピ入力\",\n    \"operator.integrateddynamics.virtual.recipesbyinput\": \"入力によるレシピ\",\n    \"operator.integrateddynamics.virtual.recipesbyoutput\": \"出力によるレシピ\",\n    \"operator.integrateddynamics.ingredients\": \"材料\",\n    \"operator.integrateddynamics.ingredients.basename\": \"材料 %s\",\n    \"operator.integrateddynamics.ingredients.items\": \"材料アイテム\",\n    \"operator.integrateddynamics.ingredients.items.info\": \"アイテムのリスト\",\n    \"operator.integrateddynamics.ingredients.fluids\": \"材料流体\",\n    \"operator.integrateddynamics.ingredients.fluids.info\": \"流体のリスト\",\n    \"operator.integrateddynamics.ingredients.energies\": \"材料エネルギー要素\",\n    \"operator.integrateddynamics.ingredients.energies.info\": \"エネルギー要素のリスト\",\n    \"operator.integrateddynamics.ingredients.with_item\": \"アイテム付き材料\",\n    \"operator.integrateddynamics.ingredients.with_item.info\": \"指定された材料の位置に指定されたアイテムを含む指定された材料のコピーを取得\",\n    \"operator.integrateddynamics.ingredients.with_fluid\": \"流体入り材料\",\n    \"operator.integrateddynamics.ingredients.with_fluid.info\": \"指定された成分の位置にある指定された流体で指定された成分のコピーを取得\",\n    \"operator.integrateddynamics.ingredients.with_energy\": \"エネルギー入り材料\",\n    \"operator.integrateddynamics.ingredients.with_energy.info\": \"指定された材料の位置に指定されたエネルギーを持つ材料のコピーを取得\",\n    \"operator.integrateddynamics.ingredients.with_items\": \"アイテム付き材料\",\n    \"operator.integrateddynamics.ingredients.with_items.info\": \"指定された材料の位置にある指定されたアイテムのリストで、指定された材料のコピーを取得\",\n    \"operator.integrateddynamics.ingredients.with_fluids\": \"流体入り材料\",\n    \"operator.integrateddynamics.ingredients.with_fluids.info\": \"指定された成分の位置にある指定された流体のリストとともに、指定された成分のコピーを取得\",\n    \"operator.integrateddynamics.ingredients.with_energies\": \"エネルギー要素入り材料\",\n    \"operator.integrateddynamics.ingredients.with_energies.info\": \"指定された成分の位置にあるエネルギー要素のリストとともに、指定された成分のコピーを取得\",\n    \"operator.integrateddynamics.recipe\": \"レシピ\",\n    \"operator.integrateddynamics.recipe.basename\": \"レシピ %s\",\n    \"operator.integrateddynamics.general\": \"一般\",\n    \"operator.integrateddynamics.general.basename\": \"一般 %s\",\n    \"operator.integrateddynamics.general.choice\": \"選択\",\n    \"operator.integrateddynamics.general.choice.info\": \"最初の値がtrueの場合は2番目の値が使用され、そうでない場合は3番目の値が使用されます。\\\\n型2と型3は等しくなければなりません。\",\n    \"operator.integrateddynamics.general.identity\": \"アイデンティティ\",\n    \"operator.integrateddynamics.general.identity.info\": \"入力値のコピーです。\",\n    \"operator.integrateddynamics.general.constant\": \"定数\",\n    \"operator.integrateddynamics.general.constant.info\": \"最初の入力値のコピーです。\",\n    \"operator.integrateddynamics.general.isnull\": \"ヌルか\",\n    \"operator.integrateddynamics.general.isnull.info\": \"指定された値がnullの場合\",\n    \"operator.integrateddynamics.general.isnotnull\": \"ヌルでないか\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"指定された値がnullでない場合\",\n    \"operator.integrateddynamics.virtual\": \"仮想演算子\",\n    \"operator.integrateddynamics.virtual.basename\": \"仮想 %s\",\n    \"operator.integrateddynamics.cast\": \"キャスト\",\n    \"operator.integrateddynamics.cast.basename\": \"キャスト\",\n    \"operator.integrateddynamics.cast.tooltip\": \"%sから%sにキャスト\",\n    \"operator.integrateddynamics.parse\": \"解析\",\n    \"operator.integrateddynamics.parse.basename\": \"解析\",\n    \"operator.integrateddynamics.parse.tooltip\": \"解析 %s\",\n    \"info_book.integrateddynamics.section.main\": \"Integrated Dynamicsについて\",\n    \"info_book.integrateddynamics.tag_index\": \"&o索引&r\",\n    \"info_book.integrateddynamics.introduction\": \"はじめに\",\n    \"info_book.integrateddynamics.introduction.text1\": \"長い間、自動化の可能性はあまりにも限られていました。&1Integrated Dynamics&0は、ロジックシステムを自動化するための頼りになるModになることを目指しています。このModは、フックと&lマインクラフト&rとその修正のほとんどの要素のサポートを備えた、表現力豊かな没入型ロジックプログラミング体験を提供することでこれを実現します。\",\n    \"info_book.integrateddynamics.introduction.text2\": \"レッドストーン回路は、常に非常に基本的でありながら、実用的で理解しやすい自動化方法です。しかし、レッドストーンの基本は不便でもあります。より複雑な自動化ネットワークの場合、レッドストーン回路は非常に大きくなり、その結果、管理も困難になります。さらに、小さな水漏れが1つあるだけで、すべてのレッドストーン愛好家にとって大きな頭痛の種になります。\",\n    \"info_book.integrateddynamics.introduction.text4\": \"下部にある矢印をクリックすると、この本内を移動できます。Shift キーを押しながらクリックすると、ページ単位ではなくセクション全体をめくることができます。大きなセクションの先頭には、より速く参照できるようにサブセクションへのハイパーリンクを含むコンテンツ テーブルが表示されます。レシピに出会ったら、この本で説明されているアイテムをクリックして、説明されているセクションをすばやく参照できます。この本の左上には、現在のセクションの上のセクションに移動するためのボタンもあります。右上隅のボタンをクリックすると、以前にアクセスしたページに戻ります。\",\n    \"info_book.integrateddynamics.tutorials\": \"チュートリアル\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"はじめに\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"このパートには、Modの基本を理解するのに役立つチュートリアルがいくつか含まれています。さらに、各チュートリアルの成果を達成すると、いくつかの&l報酬&rを獲得できます。\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"メンリルの基礎\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"このModを使い始めるには、&lメネグリンバイオーム&rで最も簡単に見つかる&lメンリルの木&rをいくつか見つける必要があります。\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"この処理を繰り返す場合は、レッドストーン信号を適用して&l圧搾器&rをリセットできます。\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"将来的には、これらのマシンのアップグレード版(自動化が容易だがコストが高いもの)も作成できるようになります。&l機械式圧搾器&rと&l機械式乾燥槽&r です。\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"ネットワークの基礎\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"このModの主な目的は、自動化のための制御ネットワークを構築することです。これは&lロジックケーブル&rを使用して行われます。これらのケーブルを10本使用してネットワークを作成してみてください。\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"次のステップでは、いくつかの&l変数カード&rと&l変数変換器&rを作成します。&l変数カード&rは、ネットワーク内の値への参照を格納するために使用されます。&l変数変換器&rは、これらの値を読み書きするために&lロジックケーブル&rに配置できるコンポーネントのクラフト要素です。\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"基本的なネットワークコンポーネント\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"&l変数カード&rを特定の値に割り当てる方法の1つは、&lリーダー&rから値を読み込むことです。まずは基本的なリーダーを作成してみましょう。その後、&l変数カード&rの値を表示するのに役立つ&l表示パネル&rを作成します。\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"アスペクト\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"簡単な例として、レッドストーンの値を読み込んで表示してみましょう。\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"その後、&lレッドストーンリーダー&rを右クリックしてGUIを開き、&l変数カード&rを&6整数&0の&lレッドストーン値&rアスペクトに挿入して、対象のレッドストーン値をその&l変数カード&rに割り当てる必要があります。\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"次のステップとして、&lレッドストーンライター&rを作成し、レッドストーンリーダーによって読み取られた最大整数のレッドストーン値を書き込むようにします。\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"ロジック演算\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"リーダーから生成された値を表示することは便利ですが、&2演算子&0を使用して特定の値を他の値と組み合わせると、非常に興味深いものになります。すべての&2演算子&0は&lロジックプログラマー&rにあります。\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"まず、アイテム形式とブロック形式の両方で存在する&lロジックプログラマー&rを作成します。 GUIを開いて、すべての&lプログラマー&rのリストを表示します。\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"次に、&6整数&0の演算子を検索してクリックします。すると、数値を入力するウィンドウが開きます。これが完了したら、空の &l変数カード&r を挿入して、その&6整数&0を&l変数カード&rに割り当てます。完了したら、別の&6整数&0に対して同じ操作をもう一度行います。\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"最後に、&l加算&rの演算子を検索して開きます。これにより、2つの&e数値&0を加算できます。2つの&6整数&0の&l変数カード&rをこれらのスロットに挿入し、新しい&l変数カード&rに割り当てます。\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"レシピ処理\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text1\": \"&oレシピ&rは&lマインクラフト&rの大部分を占めています。そのため、このModは&8レシピ&0値と、入力および出力コレクションを表す&l材料&r値を介して、レシピに対するファーストクラスのサポートを提供します。このチュートリアルでは、レシピの取得方法と使用方法について説明します。\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text2\": \"&8レシピ&0を入手する最も簡単な方法は、レシピを処理できるマシンからそれを読み取ることです。まずは、&lマシンリーダー&rを &l作業台&rに配置してレシピのリストを読み、すべてのクラフト レシピを読み取るところから始めましょう。\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text3\": \"レシピをより細かく制御したい場合、または&lマシンリーダー&r経由でレシピを公開しないマシンを使用している場合は、&lロジックプログラマー&rで自分で&8レシピ&0を視覚的に作成できます。&lチェスト&rをクラフトするための簡単なレシピを作成してみましょう。\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text4\": \"チュートリアルの最後のパートでは、レシピの読み取りをさらに一歩進めます。&l出力によるレシピ&rのアスペクトを使用すると、&8材料&0のインスタンスに適用すると、指定された&8材料&0を出力として返す最初の&8レシピ&0を返す演算子を取得できます。これを使用して、&lチェスト&rのレシピを検索します。\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text5\": \"まず、&lロジックプログラマー&rで&8チェスト&0を1つだけ含む&8材料&0のインスタンスを作成します。次に、&l作業台&rを対象とする&lマシンリーダー&rから&l出力によるレシピ&rのアスペクトを読み取ります。その後、&o適用&r演算子を使用して、&lマシンリーダー&rから取得した演算子に&lチェスト&rの&8材料&0を適用します。最後に、結果の変数を&l表示パネル&rに挿入して、&lチェスト&rのレシピを確認します。(使用したすべての変数を &l変数ストア&rに挿入することを忘れないでください)\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text6\": \"明らかに、&8レシピ&0と&8材料&0でできることは他にもたくさんあります。&lマシンリーダー&rと&lロジックプログラマー&rを見て、他にどのような高度な機能が利用できるかを確認してください。\",\n    \"info_book.integrateddynamics.tutorials.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.tutorials.nbt.text1\": \"&4NBT&0タグは複雑なデータ構造を表すために使用されます。これらは通常、マインクラフトの内部でマシンやエンティティの情報を格納するために使用されます。このチュートリアルでは、簡単なNBTタグの読み取りと操作について説明します。\",\n    \"info_book.integrateddynamics.tutorials.nbt.text2\": \"最初のステップとして、書見台で&lブロックリーダー&rを使用して、その&4NBT&0データを読み取り、&l表示パネル&rで視覚化します。\",\n    \"info_book.integrateddynamics.tutorials.nbt.text3\": \"次に、読み取った&l書見台&rの&4NBT&0タグから、&3本&0のサブタグを複合タグとして読み取り、可視化します。\",\n    \"info_book.integrateddynamics.tutorials.nbt.text4\": \"最後のステップとして、この&3本&0のサブタグから&3ID&0フィールドを読み取ります。ただし、最後に読み取ったサブタグから開始するのではなく、ルートから開始して&lNBTパス&rの式を適用します。このような式は、深くネストされた&4NBT&0値を取得するのに役立ちます。たとえば、式&4\\\".root.child1.child2\\\"&0を&3NBT&0タグ&3{ root: { child1: { child2: ”some value” }}}&0に適用すると、&3”some value”&0 が出力されます。&lNBTパス&rのより高度な機能については、マニュアルの高度な部分で後ほど説明します。\",\n    \"info_book.integrateddynamics.tutorials.nbt.text5\": \"最終的な進捗を達成するには、&3本&0/&3ID&0フィールドを選択する&lNBTパス&rの式を作成し、それを &l書見台&rタグに適用し、&l表示パネル&rで視覚化します。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"高度なネットワークコンポーネント\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"基本的なロジックについて理解できたので、次はより高度なネットワーク コンポーネントである&lマテリアライザー&rと&lプロキシ&rを見てみましょう。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"ご存知のとおり、&lロジックプログラマー&rを使用すると、&o静的&r値を持つ変数を作成したり、&o動的&r値の評価に基づいてより複雑な変数を作成したりできます。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"&lマテリアライザー&rは、&o動的&r値を&o静的&r値に変換するために使用できます。これは、&o動的&rの&l変数カード&rを&lマテリアライザー&rの上部のスロットに挿入することで実行できます。左側のスロットに挿入されたすべての&l変数カード&rには、その時点で上部の変数が持つ値が設定されます。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"&lプロキシ&rを使用すると、変数にさらに間接的なレベルを追加できます。たとえば、&o静的&r値を必要とするロジックプログラムがあるとします。将来その値を変更する場合は、その値が&oプロキシ化&rでない限り、プログラムを書き直す必要があります。その場合は、&lプロキシ&rの値を更新するだけで済みます。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"このチュートリアルでは、具現化及びプロキシ化された&l変数カード&rを作成します。\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"高度な演算子\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"前回のチュートリアルで気づいたかもしれませんが、&lロジックプログラマー&rを使用すると、数値を追加するだけでなく、さまざまな操作を実行できます。このチュートリアルでは、より高度なデータ構造と演算子の連鎖について説明します。\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"まず、&6整数&0 &61&0、&610&0、&6100&0 の静的な&7リスト&0を作成し、それを&l表示パネル&rで視覚化します。\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7アイテム&0は、このModで利用できる値型のひとつです。静的な&7アイテム&0の&l変数カード&rを作成し、そのアイテムからMod名を動的に取得して、&l表示パネル&rに可視化してみましょう。\\n\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"参照済みの変数を格納するためには、&l変数ストア&rを使用する必要があることを忘れないでください。\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"演算子の値\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"最初のステップとして、&l論理プログラマー&r に移動し、オートコンプリート ドロップダウン リストに &oArithmetic Addition&r &2Operator&0 と入力してクリックし、新しい静的な &2Operator&0 を作成して、&lVariable Card&r に書き込みます。その後、2 つの &6Integer&0 変数を作成します。次に、&oApply&r 演算子を見つけて、最初の &6Integer&0 を &2Operator&0 に適用します。これにより、加算 &2Operator&0 の最初の入力引数がその &6Integer&0 に &obind&r され、残りの 1 つの入力引数を持つ新しい &2Operator&0 が出力されます。次に、&oApply&r 演算子を再度使用して、新しく作成した &2Operator&0 の最後の入力引数を &obind&r します。最後に、&lDisplay Panel&r を使用して、動的に呼び出された加算を視覚化します。\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"&2演算子&0を適用する以外にも、高度な&2演算子&0操作はたくさんあります。次に、&7リスト&0での&2演算子&0の使用、具体的には &7リスト&0と&o述語&r(1つの入力引数と&9ブール&0出力値を持つ&2演算子&0)を受け取る&oフィルター&r&2演算子&0について説明します。まず、&6整数&0&61&0、&610&0、および &6100&0の&7リスト&0を作成します。次に、&o関係イコール&r&2演算子&0を変数に格納します。次に、この演算子を&6整数&0&610&0に適用します。次に、&7リスト&0と適用した&2演算子&0で&oフィルター&r&2演算子&0を使用します。これにより、指定された演算子を適用して、&9False&0を返す&7リスト&0のすべてのアイテムがフィルター処理されます。この場合、&610&0と等しくないすべての要素がリストから削除されます。結果を&l表示パネル&rで視覚化します。\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"&2演算子&0には、&o述語&rの結合、&2演算子&0引数の反転、&7リスト&0に対するその他の操作など、他にも多数の操作があります。\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"課題\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"この最後のチュートリアルセクションには、&1Integrated Dynamics&0の基本を理解していることを示すいくつかの重要な課題が含まれています。各課題についてこれ以上の情報は提供されませんので、頑張ってください！\",\n    \"info_book.integrateddynamics.manual\": \"マニュアル\",\n    \"info_book.integrateddynamics.manual.introduction\": \"はじめに\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"前提条件\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"メンリルの木\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"時々、背の高い青い木に出会うことがあります。そのような木は&lメンリルの木&rと呼ばれ、非常に興味深い特性を持っています。まず、葉が光を放ちます。これは光るベリーによるもので、とてもおいしいです！次に、この木の美しい木材には樹脂がたくさん含まれており、このModのほとんどのものを作るのに使用できます。\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"メネグリンバイオーム\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"残念ながら、&lメンリルの木&rを見つけるのは非常に困難です。幸いなことに、これらの木が繁茂するバイオームは1つあり、&lメネグリンバイオーム&rです。大量の&lメンリルの木&rがあると、このバイオームの一部が青くなります。これは、これらの木が環境に放出する&lメンリル樹脂&rによるものです。\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"圧搾器\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"乾燥槽\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"ブロックやアイテムをを乾燥させるには、&l乾燥槽&rを使用します。これは、たとえば、&lメンリル樹脂&rを&l結晶化したメンリル&rに乾燥させたいときに便利です。\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"絶対に熱い流体を入れないでください！\",\n    \"info_book.integrateddynamics.manual.networks\": \"ネットワーク\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"&lIntegrated Dynamics&rの&lネットワーク&rは、あらゆる優れた自動化システムの基礎です。ネットワークは、接続された&lロジックケーブル&rのコレクションで構成され、パーツをその上に配置できます。これらのケーブルは、&lレンチ&rを使用して管理する必要があります。ケーブルまたはパーツをShiftキーを押しながら右クリックすると削除され、通常の右クリックでは他のケーブルから切断されます。\",\n    \"info_book.integrateddynamics.manual.logic\": \"ロジック\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"値の種類\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean\": \"ブール\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"最も単純な値型で、その値は&9True&0または&9False&0のいずれかになります。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer\": \"整数\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"正の数と負の数を表す値型です。たとえば、&6-20&0、&60&0、&61000&0、&6333&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"値の範囲は&6–2147483648&0から&62147483647&0までです。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"この値型は、 &l数値&rカテゴリの一部です。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double\": \"ダブル\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"実数で、正または負になることもあります。たとえば、&e3.1415&0、&e4815162342&0、&e3.33333333&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"この値型は、 &l数値&rカテゴリの一部です。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long\": \"ロング\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"&6整数&0 と同じですが、値の範囲が広く、&e-9223372036854775808&0から&e9223372036854775807&0までになります。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"この値型は、 &l数値&rカテゴリの一部です。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string\": \"文字列\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"文字のシーケンスで、たとえば、&4”aaa”&0、&4“こんにちは！”&0、&4“x!(àà)ç”&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list\": \"リスト\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator\": \"演算子\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"&2演算子&0への参照を含む値で、演算子は&lカリー化&rの概念に基づいて、より柔軟な評価のために部分的に適用できます。これは&lメタプログラミング&r(調べてみてください、これは実際にあるものです！)の概念の一部であり、演算子は他の演算子によってデータとして扱われます。これにより基本的に新しい演算子を自分で作成し、より複雑な操作に使用できるようになります。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"たとえば、加算&2演算子&0を変数カードに保存できます。これを行うには演算子名を入力し始め、自動補完ドロップダウンリストから演算子を選択してクリックします。次に、1つの&6整数&0の&l変数カード&rを使用してそれを部分的に適用できます。これにより、1つの入力タイプと1つの出力タイプを持つ新しい仮想演算子が作成され、内部には適用された&6整数&0への参照が含まれます。別の&6整数&0を適用すると、&6整数&0が作成されます。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"&2演算子&0の他の操作は、リストを単一の値に減らしたり、&l述語&rに基づいてリストをフィルター処理したりするなど、より高度な評価のために&lロジックプログラマー&rに存在します。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"&lマインクラフト&r内の特定のものは、内部的に&3NBT&0データ構造を使用します。&3NBT&0タグは異なるタイプの複数の値を保持できる特殊な種類のオブジェクトです。たとえば、すべてのアイテムを含む&8チェスト&0のNBT表現、&8ツルハシ&0のエンチャント情報などです。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"変数はこのような&3NBT&0タグへの参照を保持することができ、これらのタグを読み取ったり拡張したりするための対応する演算子が存在します。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"&3NBT&0タグは通常、専用の演算子を使用して特定の情報を読み取ることができない場合に備えて、このModで低レベルのフォールバックメカニズムとして使用されます。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block\": \"ブロック\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"&lマインクラフト&rのブロックへの参照で、たとえば、&8土&0、&8丸石&0、&8赤色のテラコッタ&0、&8チェスト&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"アイテム\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"&lマインクラフト&rのアイテムへの参照で、たとえば、&8リンゴ&0、&8丸石&0、&8ダイヤモンドのツルハシ&0、&8エンチャントされた剣&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"エンティティ\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"特定の&lマインクラフト&rのエンティティへの参照で、たとえば、&8ゾンビ&0、&8ウシ&0、&8プレイヤー_abc&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"流体\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"&lマインクラフト&rの流体への参照で、たとえば、&8溶岩@1000mB&0、&8水@3mB&0、&8メンリル樹脂@3030mB&0など\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"材料\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients.text1\": \"&lアイテム&r、&l流体&r、&lエネルギー量&r(&6整数&0)のコレクションで、主な用途は、&8レシピ&0の入力と出力を表すことです。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"レシピ\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text1\": \"&8材料&0を入力として受け取り、別の&8材料&0の値を出力する抽象的なクラフトレシピです。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text2\": \"レシピは、&lロジックプログラマー&rを使用して視覚的に作成することも、&lマシンリーダー&rを使用して(サポートされている)マシンから読み取ることもできます。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text3\": \"これらのレシピは、&lIntegrated Crafting&rなどの自動クラフトModに使用できます。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text4\": \"GUI内の既存の&l変数カード&rの中から特定のレシピをすばやく見つけるには、Shiftキーを押したままにして、レシピの出力アイテムを一時的に表示します。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text5\": \"&lロジックプログラマー&r内の入力スロットをShiftキーを押しながらクリックすると、この入力スロットの詳細なプロパティを設定できます。たとえば、NBTを考慮するかどうか、このアイテムを複数のレシピで再利用できるかどうか(クラフトジョブ後に完全に消費されない場合など)、このアイテムに対してタグベースの代替品を考慮するかどうかを指定できます。\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"変数\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"変数カード\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&l変数カード&rは、このModにおけるロジック転送の主なモードです。変数には、特定の&l値型&rを持つ特定の&l値&rへの参照が含まれます。\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text2\": \"変数は静的または動的のいずれかになります。静的変数は一度定義されると、その後変更されることはありません。動的変数は、何らかの形式の&lロジック評価&rの結果であり、&l演算子&rまたは&lリーダーアスペクト&rによって実行されます。動的変数の例としては、継続的に変化する&lマインクラフト&rのワールドの時間があります。\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"各&l変数カード&rには一意のIDがあり、ネットワーク内で&lロジック評価&rのために内部的に使用されます。ネットワークでエラーが発生すると、デバッグに役立つようにこれらのIDがプレイヤーに表示されます。使いやすさを向上させるために、&lラベラー&rを使用して&l変数カード&rにラベルを付けることができます。これにより、プレイヤーはIDではなくラベルを見ることができます。\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"変数ストア\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"一部の動的変数は他の変数に基づいて評価されますが、その場合、参照先の変数へのアクセスが可能でなければなりません。他の2つの変数の追加を表す動的変数は、評価時に他の2つの変数にアクセスできる必要があります。他の変数へのアクセスは、動的変数が評価されているのと同じネットワーク内で可能でなければなりません。これは、他の変数カードを&l変数ストア&rに配置することで実現できます。ネットワークには複数の&l変数ストア&rが存在する場合があります。これらの他の変数は、同じネットワークに存在する必要がある特定の変数を参照することもできます。\",\n    \"info_book.integrateddynamics.manual.logic.advanced\": \"上級者向け\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path\": \"NBTパス\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text3\": \"たとえば、式&4\\\".root.child1.child2”&0を&3NBT&0タグ&3{ root: { child1: { child2: ”some value” }}}&0に適用すると、&3”some value”&0が出力されます。\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text4\": \"フィールド名の配列またはワイルドカードを指定することで、複数のフィールドを選択できます。例: &4[fieldA,fieldB]&0または&4*&0\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text5\": \"たとえば、式&4\\\".a[b1,b2]*”&0を&3NBT&0タグ&3{ a: { b1: { c: ”some value” }}}&0に適用すると、&3”some value”&0が出力されます。\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text6\": \"NBTリストを操作する場合、&4[1]&0などのインデックスを指定して特定のエントリを選択できます。\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text7\": \"たとえば、式&4\\\".a[1]”&0を&3NBT&0タグ&3{ a: [10,20] }&0に適用すると、&3”20”&0が出力されます。\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text8\": \"スライス演算子を使用して、複数のリストインデックスを選択できます: &4[start:end:step]&0 (開始のみが必須で、他の要素は省略できます)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text9\": \"たとえば、式&4\\\".a[1:4:2]\\\"&0を&3NBT&0タグ&3{ a: [0,1,2,3,4,5] }&0に適用すると、&3[1,3]&0が出力されます。\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text10\": \"フィルター構文を使用すると、より高度なフィルター式も使用できます。たとえば、&4[?(@.childName < 10)]&0(@は現在のタグ、..は親タグ、$はルートタグを参照します)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text11\": \"たとえば、式&4”$.a[?(@ == 3)]”&0を&3NBT&0タグ&3{ a: [0,1,2,3,4,5] }&0に適用すると、&3[3]&0が出力されます。\",\n    \"info_book.integrateddynamics.manual.parts\": \"パーツ\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"はじめに\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text3\": \"パーツの&lエネルギーチャンネル&rは、パーツがエネルギーを引き出せる場所を決定します。-1はワイルドカードチャンネル(すべてのチャンネルに一致)、0はデフォルトチャンネル(チャンネル設定のないエネルギーセルなどのパーツで使用)です。最後に、&l対象サイド&rは対象ブロックのどの側面と相互作用するかを決定します。これは実際に配置されている側面がデフォルトになります。\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text4\": \"一部のリーダーおよびライターのパーツには、特定の他のModが存在する場合にのみアスペクトが含まれます。特定のパーツにアスペクトが存在しない場合は、そのパーツを作成しようとするたびにアイテムのツールチップに警告が追加されます。\",\n    \"info_book.integrateddynamics.manual.parts.settings\": \"設定\",\n    \"info_book.integrateddynamics.manual.parts.settings.text1\": \"ほとんどのパーツには&l設定&r画面があり、これを使用して基本的なパーツ設定を変更できます。\",\n    \"info_book.integrateddynamics.manual.parts.settings.text3\": \"ティック/操作では、このパーツのティック周波数を設定できます。高いほど、動作が遅くなります。\",\n    \"info_book.integrateddynamics.manual.parts.settings.text4\": \"優先度は、単一のネットワークティック内でこのパーツが実行される順序を決定します。\",\n    \"info_book.integrateddynamics.manual.parts.settings.text5\": \"エネルギーチャンネルは、この部分がティックするときにエネルギーを消費するチャンネルを示します。これは、ネットワークのエネルギー消費が有効になっている場合にのみ適用されます。\",\n    \"info_book.integrateddynamics.manual.parts.offsets\": \"オフセット\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text2\": \"オフセットは常にパーツの対象位置を基準とし、適用されているパーツ拡張機能の数によって制限されます。\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text4\": \"オフセットは静的に定義できるだけでなく、&l変数カード&rを使用して&o動的&rに定義することもできます。\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"リーダー\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"はじめに\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction.text1\": \"動的変数を作成する最も簡単な方法は、&lリーダー&rを使用することです。ワールドから情報を読み取り、それを&l変数カード&r内に保存するためのさまざまな種類のリーダーが存在します。各&lリーダー&rには、異なる&l値型&rを持つさまざまなものを読み取るための1つ以上の&lアスペクト&rが含まれています。\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"オーディオ\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"環境で再生されている音楽とオーディオに関する情報です。\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"ブロック\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"パーツが向いているブロックに関する情報です。\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"エンティティ\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"パーツが向いている四角形内にあるエンティティに関する情報です。\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"拡張ディメンション\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"オンラインプレイヤーのリストなど、サーバーに関する情報です。\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"流体\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"このパーツが向いている流体タンクから値を読み取ります。\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"インベントリ\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"スロットやアイテムの数などの、対面インベントリからの情報を読み取ります。アイテム自体は転送されません。\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"マシン\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"このパーツが向いているマシンで行っている作業などを読み取ることができます。\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"ネットワーク\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"このパーツが接続している&lIntegrated Dynamics&rネットワークに関する情報を読み取ります。これは、このパーツが接続されているネットワークと同じネットワークである必要はありません。\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"レッドストーン\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"対面ブロックからレッドストーン情報を読み取ります。\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"ワールド\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"現在の天気や時間など、このパーツが存在するワールドに関する情報です。\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"ライター\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"オーディオ\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"音と音楽を再生します。\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"エフェクト\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"特定の値と設定に基づいてパーティクルを放出します。\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"エンティティ\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"エンティティを構成および制御します。\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"マシン\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"マシンを構成および制御します。\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"インベントリ\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"インベントリとパイプに関連するアクションを実行します。アイテム自体は転送されません。\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"レッドストーン\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"特定のレッドストーンレベルを出力します。\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"その他\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"ライト\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"常にオンになっているか、&1ブール&0または&6整数&0の&l変数カード&rを使用して制御できるシンプルなライトです。\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"ディスプレイ\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"&l変数カード&rの値を表示するだけの便利な監視またはデバッグ ツールです。&lディスプレイ&rは&lレンチ&rで右クリックして回転できます。\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text2\": \"GUI内では、現在表示されている値をクリップボードにコピーして、ゲーム外で使用できるようにするための&lコピー&rボタンが用意されています。また、OSのデフォルトのコピーホットキーを使用してこの値をコピーすることもできます。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"単方向コネクター\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"場合によっては、ロジックネットワークが長距離に及ぶことがあります。通常のケーブルでも十分ですが、長距離にわたってケーブルを配線するのはあまり便利ではないかもしれません。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&l単方向コネクター&rを使用すると、長距離のネットワークをワイヤレスで接続できます。これは、1つのネットワークに最初のコネクターを配置し、別のネットワークに2番目のコネクターを配置することで実現できます。これらの2つのコネクターは、互いに直線上にあり、距離は512ブロック以下である必要があります。有効な接続が確立されると、コネクターはアクティブとして表示されます。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"全方向コネクター\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"&l単方向&rコネクターは、一方向の長距離接続に役立ちますが、場合によっては少し限られている場合があります。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"たとえば、単一のコネクターを使用して2つ以上のネットワークを接続したり、他のディメンションのネットワークに接続したりする必要がある場合があります。このような場合は、&l双方向&rコネクターを使用できます。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"これらのコネクターは、最初に作成されたときに一意のグループIDを持ち、ネットワークを接続できるグループを識別します。各コネクターグループは、ワールドに配置されたときに一意のパーティクルカラーを持ちます。グループIDは、アイテムのツールチップに表示されるか、配置時にコネクターを右クリックすると表示されます。グループ内の既存のコネクターを使用して新しいコネクターを作成することで、グループにコネクターを追加できます。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"既存のコネクターのグループを変更するには、2つの方法があります。作業台で1つだけクラフトすると、他のメンバーのない新しいグループに割り当てられ、2つを一緒にクラフトすると、最初のコネクターのグループが2番目のコネクターにコピーされます。\",\n    \"info_book.integrateddynamics.manual.machines\": \"マシン\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"マテリアライザー\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"動的&l変数カード&rを静的にするには、&lマテリアライザー&rを使用できます。動的&l変数カード&rを挿入し、新しい空の&l変数カード&rを挿入する必要があります。これにより、動的&l変数カード&rの現在の値が静的な値として含まれるようになります。\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"これは、特定の動的&l変数カード&rの値をログに記録したい場合や、&l変数カード&rの動的評価が不要になった場合に役立ちます。\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&l変数カード&rは&lマテリアライザー&rにパイプで入出力できます。\",\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"プロキシ\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"&lプロキシ&rを使用すると、特定の&l変数カード&rへの動的な参照を作成できます。これにより、最上位のスロットに&l変数カード&rを挿入し、新しい&l変数カード&rを書き込むことができます。この新しい&l変数カード&rは、プロキシに存在する&l変数カード&rの値を動的に取得します。プロキシが機能するには、参照される(またはその他の)&l変数カード&rが最上位のスロットに残っている必要があることに注意してください。\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"これは一連の&l変数カード&rがあり、1つの値を手動で変更したい場合に便利です。その変数をプロキシするといつでも別の変数と交換できるようになります。\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"&lマテリアライザー&rと同様に、&l変数カード&rをパイプで入出力できます。\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"状況によっては、何らかの変数値の履歴を保持する必要がある場合があります。たとえば、最後の値の平均または差を取得したい場合などです。\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"エネルギーバッテリー\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"&lIntegrated Dynamics&rのネットワークは、エネルギーを消費するように構成できます。そのためにはネットワークに&lエネルギーバッテリー&rを接続して、エネルギー貯蔵容量を追加できます。\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"レッドストーン信号を適用すると、すべての側面へのエネルギーの自動エクスポートモードが有効になります。\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"このバッテリーがインベントリに保持されている場合、Shiftキーを押しながら右クリックすると、&l自動供給&rモードが有効になります。有効にすると、このバッテリーは両手に持っているアイテムに自動的にエネルギーを補給しようとします。\",\n    \"info_book.integrateddynamics.manual.machines.generator\": \"発電機\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"機械式圧搾器\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"機械式乾燥槽\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"&l乾燥槽&rでの乾燥には時間がかかります。乾燥を早く終わらせたい場合や、より多くのものを乾燥させたい場合は、稼働に電力を必要とする&l機械式乾燥槽&rを使用することができます。\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"ロジックプログラミング\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"動的変数カードを作成するもう1つの方法は、1つ以上の&l変数カード&rに&l演算子&rを適用して、新しい&l変数カード&rを作成することです。&l演算子&rの例としては、2つの&l数値&rを追加して新しい&l数値&rを作成することが挙げられます。\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"&lロジックプログラマー&rには、多数の&l演算子&rが含まれています。また、すべての&l値型&rに対して静的&l変数カード&rを作成することもできます。&l演算子&rを選択したら、有効な&l変数カード&rを想定される入力スロットに挿入する必要があります。最後に、別の&l変数カード&rを出力スロットに挿入する必要があります。&l演算子&rが有効な場合は、新しい動的変数がカードに書き込まれます。参照される&l変数カード&rは、&l演算子&rが評価される同じネットワーク内の&l変数ストア&rに存在する必要があることに注意してください。\",\n    \"info_book.integrateddynamics.manual.diagnostics\": \"ネットワーク診断\",\n    \"info_book.integrateddynamics.manual.diagnostics.text1\": \"オペレーターのステータスを持つプレイヤーは、&lネットワーク診断&rツールにアクセスでき、サーバー上のすべてのネットワークのティック時間を検査できます。\",\n    \"info_book.integrateddynamics.manual.diagnostics.text2\": \"このツールを有効にするには、コマンド&o\\\\integrateddynamics networkdiagnostics&rを実行して、一時的なローカルWebサーバーを起動します。\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/ko_kr.json",
    "content": "{\n    \"itemGroup.integrateddynamics\": \"Integerated Dynamics\",\n    \"general.integrateddynamics.description\": \"기계를 완전 자동으로 제어하세요.\",\n    \"general.integrateddynamics.info.none\": \"없음\",\n    \"general.integrateddynamics.energy\": \"에너지\",\n    \"general.integrateddynamics.error.no_network\": \"유효한 네트워크를 찾을 수 없습니다.\",\n    \"general.integrateddynamics.true\": \"참\",\n    \"general.integrateddynamics.false\": \"거짓\",\n    \"gui.integrateddynamics.part_settings\": \"파트 설정\",\n    \"gui.integrateddynamics.aspect_settings\": \"측면 설정\",\n    \"gui.integrateddynamics.keybinding\": \"키 설정\",\n    \"gui.integrateddynamics.button.save\": \"저장\",\n    \"gui.integrateddynamics.button.rename\": \"이름 바꾸기\",\n    \"gui.integrateddynamics.button.copy\": \"복사\",\n    \"gui.integrateddynamics.button.copy.info\": \"클립보드에 복사\",\n    \"gui.integrateddynamics.button.edit\": \"편집\",\n    \"gui.integrateddynamics.button.add\": \"추가\",\n    \"gui.integrateddynamics.button.remove\": \"제거\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"틱/작업\",\n    \"gui.integrateddynamics.partsettings.priority\": \"우선도\",\n    \"gui.integrateddynamics.partsettings.channel\": \"에너지 채널\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"이 서버에서는 네트워크 에너지 소비가 비활성화 되어있습니다.\",\n    \"gui.integrateddynamics.partsettings.side\": \"목표 면\",\n    \"gui.integrateddynamics.delay.capacity\": \"용량\",\n    \"gui.integrateddynamics.part.properties\": \"속성:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"필터\",\n    \"gui.integrateddynamics.rewards\": \"보상\",\n    \"gui.integrateddynamics.rewards.collect\": \"받기\",\n    \"gui.integrateddynamics.rewards.collected\": \"받음\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"보상을 받을 수 없습니다. 보관함이 가득 찼나요?\",\n    \"gui.integrateddynamics.input\": \"입력: %s\",\n    \"gui.integrateddynamics.output\": \"출력: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"유체 자동 배출\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"유체 자동 배출: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"인접한 탱크로 보냅니다.\",\n    \"gui.integrateddynamics.input_short\": \"입력\",\n    \"gui.integrateddynamics.output_short\": \"출력\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"엄격한 NBT\",\n    \"gui.integrateddynamics.recipe.tagvariants\": \"아이템 태그 변수\",\n    \"gui.integrateddynamics.diagnostics.title\": \"네트워크 진단\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"부품\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"저장고 관측기\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"네트워크\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"케이블\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"부품\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"틱 시간 (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"차원\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"위치\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"면\",\n    \"key.categories.integrateddynamics\": \"통합 역학\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"논리 프로그래머 : 집중 검색\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"논리 프로그래머 : 변수 이름 재설정\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"페이지 넘기기\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"여러 페이지 넘기기\",\n    \"block.integrateddynamics.cable\": \"논리 케이블\",\n    \"block.integrateddynamics.variablestore\": \"변수 저장소\",\n    \"block.integrateddynamics.energy_battery\": \"에너지 배터리\",\n    \"block.integrateddynamics.energy_battery.info\": \"조합창에 다른 에너지 배터리와 두면 용량을 늘릴 수 있습니다. 쉬프트 + 오른쪽 클릭으로 자동 공급.\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"자동 공급: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"크레에이티브 에너지 배터리\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"자동 공급: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"논리 프로그래머\",\n    \"block.integrateddynamics.coal_generator\": \"발전기\",\n    \"block.integrateddynamics.coal_generator.info\": \"연료 아이템에서 에너지를 얻습니다\",\n    \"block.integrateddynamics.proxy\": \"프록시\",\n    \"block.integrateddynamics.proxy.info\": \"한 측면에 참조하는 변수를 노출시킵니다. 조합창에서 초기화.\",\n    \"block.integrateddynamics.materializer\": \"구체화장치\",\n    \"block.integrateddynamics.materializer.info\": \"변수의 값을 새로운 변수에 구체화합니다.\",\n    \"block.integrateddynamics.menril_log\": \"멘릴 원목\",\n    \"block.integrateddynamics.menril_log_filled\": \"강화된 멘릴 원목\",\n    \"block.integrateddynamics.menril_log_stripped\": \"껍질 벗긴 멘릴 원목\",\n    \"block.integrateddynamics.menril_leaves\": \"멘릴 나무 잎\",\n    \"block.integrateddynamics.menril_sapling\": \"멘릴 요목\",\n    \"block.integrateddynamics.menril_planks\": \"멘릴 나무 판자\",\n    \"block.integrateddynamics.menril_slab\": \"멘릴 나무 반 블록\",\n    \"block.integrateddynamics.menril_fence\": \"멘릴 나무 울타리\",\n    \"block.integrateddynamics.menril_fence_gate\": \"멘릴 나무 울타리 문\",\n    \"block.integrateddynamics.menril_wood\": \"멘릴 나무\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"껍질 벗긴 멘릴 나무\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"결정화된 멘릴 블록\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"결정화된 멘릴 벽돌\",\n    \"block.integrateddynamics.block_menril_resin\": \"멘릴 수지\",\n    \"block.integrateddynamics.drying_basin\": \"건조대\",\n    \"block.integrateddynamics.drying_basin.info\": \"아이템과 유체를 건조하는 데 쓰입니다\",\n    \"block.integrateddynamics.squeezer\": \"압착기\",\n    \"block.integrateddynamics.squeezer.info\": \"위에서 점프하면 압착하고, 평평하게하고, 부숩니다. 레드스톤 신호로 초기화.\",\n    \"block.integrateddynamics.menril_torch\": \"멘릴 횃불\",\n    \"block.integrateddynamics.menril_torch_stone\": \"멘릴 돌 횃불\",\n    \"block.integrateddynamics.invisible_light\": \"보이지않는 빛\",\n    \"block.integrateddynamics.menril_door\": \"멘릴 나무 문\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"멘릴 나무 계단\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"결정화된 멘릴 계단\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"결정화된 멘릴 벽돌 계단\",\n    \"block.integrateddynamics.delay\": \"지연기\",\n    \"block.integrateddynamics.delay.info\": \"한 측면에 변수의 기록을 노출시킵니다. 조합창에서 초기화.\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"액체 후렴과\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"결정화된 후렴과 블록\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"결정화된 후렴과 벽돌\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"결정화된 후렴과 계단\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"결정화된 후렴과 벽돌 계단\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"기계식 압착기\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"에너지로 압착하고, 평평하게하고, 부숩니다.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"기계식 건조대\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"에너지로 아이템과 유체를 건조하는 데 쓰입니다.\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"결정화된 멘릴 반 블록\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"결정화된 멘릴 벽돌 반 블록\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"결정화된 후렴과 반 블록\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"결정화된 후렴과 벽돌 반 블록\",\n    \"item.integrateddynamics.wrench\": \"렌치\",\n    \"item.integrateddynamics.variable\": \"변수 카드\",\n    \"item.integrateddynamics.variable.info\": \"조합창에서 초기화 또는 복사\",\n    \"item.integrateddynamics.variable.warning\": \"§4§l주의: 이 아이템을 중간 클릭으로 복사하지 마세요!\",\n    \"item.integrateddynamics.variable.id\": \"§e§o변수 ID: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"라벨러\",\n    \"item.integrateddynamics.labeller.info\": \"변수에 라벨을 붙입니다. 논리 프로그래머에서도 작동합니다.\",\n    \"item.integrateddynamics.labeller.button.write\": \"쓰기\",\n    \"item.integrateddynamics.facade\": \"덮개\",\n    \"item.integrateddynamics.facade.info\": \"조합대에서 블록과 합쳐집니다.\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"액체 후렴과 양동이\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"멘릴 수지 양동이\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"결정화된 멘릴 덩어리\",\n    \"item.integrateddynamics.variable_transformer\": \"입/출력 변수 변환기\",\n    \"item.integrateddynamics.variable_transformer_output\": \"출력 변수 변환기\",\n    \"item.integrateddynamics.variable_transformer_input\": \"입력 변수 변환기\",\n    \"item.integrateddynamics.menril_berries\": \"멘릴 열매\",\n    \"item.integrateddynamics.menril_berries.info\": \"맛있고 빛납니다.\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"휴대용 논리 프로그래머\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"통합의 역학에 관하여\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"튜토리얼이 포함된 완전한 모드 참조 가이드\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"결정화된 후렴과 덩어리\",\n    \"item.integrateddynamics.logic_director\": \"논리 디렉터\",\n    \"item.integrateddynamics.proto_chorus\": \"프로토 후렴과\",\n    \"biome.integrateddynamics.meneglin\": \"메네글린\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"메네글린 발견\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"메네글린 생물 군계에서 가장 쉽게 찾을 수 있는 멘릴 나무를 베어내세요.\",\n    \"advancement.integrateddynamics.squeezing\": \"상사처럼 쥐어 짜기\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"압착기를 제작하세요.\",\n    \"advancement.integrateddynamics.drying\": \"상사처럼 피말리기\",\n    \"advancement.integrateddynamics.drying.desc\": \"건조대를 제작하세요.\",\n    \"advancement.integrateddynamics.menril_production\": \"멘릴 생산\",\n    \"advancement.integrateddynamics.cables_logic\": \"논리를 위한 케이블\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"논리 케이블을 제작하세요.\",\n    \"advancement.integrateddynamics.networks_logic\": \"논리를 위한 네트워크\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"적어도 10개의 연결된 논리 케이블로 논리 네트워크를 만드세요.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"멘릴 렌치질\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"렌치를 만드세요.\",\n    \"advancement.integrateddynamics.variables\": \"값을 위한 변수\",\n    \"advancement.integrateddynamics.variables.desc\": \"변수 카드를 만드세요.\",\n    \"advancement.integrateddynamics.variable_input\": \"변수 입력 변환\",\n    \"advancement.integrateddynamics.variable_output\": \"변수 출력 변환\",\n    \"advancement.integrateddynamics.redstone_reading\": \"레드스톤 판독\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"레드스톤 판독기를 만드세요\",\n    \"advancement.integrateddynamics.block_reading\": \"블록 판독\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"블록 판독기를 만드세요.\",\n    \"advancement.integrateddynamics.inventory_reading\": \"보관함 판독\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"보관함 판독기를 만드세요.\",\n    \"advancement.integrateddynamics.machine_reading\": \"기계 판독\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"기계 판독기를 만드세요.\",\n    \"advancement.integrateddynamics.value_displaying\": \"값 표시\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"디스플레이 패널을 만드세요.\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"레드스톤 변환\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"레드스톤 §6정수§7 값용 변수 카드를 만드세요.\",\n    \"advancement.integrateddynamics.redstone_observement\": \"레드스톤 관측\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"디스플레이 패널에 §6정수§7 변수 카드를 넣으세요.\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"레드스톤 전송\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"레드스톤 작성기가 레드스톤 판독기가 읽은 최대 정수 레드스톤 레벨을 쓰도록 하십시오.\",\n    \"advancement.integrateddynamics.recipe_reading\": \"레시피 판독\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"기계 판독기를 사용하여 기계(예: 크래프팅 테이블)의 레시피 리스트를 읽으세요.\",\n    \"advancement.integrateddynamics.recipe_creation\": \"레시피 만들기\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"논리 프로그래머를 사용하여 시각적으로 §8레시피§7를 만드세요.\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"레시피 조회\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"기계 판독기를 사용하여 상자의 레시피를 동적으로 조회하세요.\",\n    \"advancement.integrateddynamics.lectern_data\": \"독서대 NBT\",\n    \"advancement.integrateddynamics.lectern_data.desc\": \"블록 판독기를 사용하여 독서대와 같은 기계에서 NBT 태그를 읽으세요.\",\n    \"advancement.integrateddynamics.lectern_book\": \"독서대 NBT 책\",\n    \"advancement.integrateddynamics.lectern_book.desc\": \"블록 판독기에서 독서대 안의 책의 NBT 필드를 독서대의 NBT 태그에서 읽으세요.\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"독서대 NBT 책 이름\",\n    \"advancement.integrateddynamics.lectern_book_name.desc\": \"블록 판독기에서 독서대 안의 책의 이름(id)를 NBT 경로 표현을 적용하여 독서대의 NBT 태그에서 읽으세요.\",\n    \"advancement.integrateddynamics.logic_programming\": \"논리 프로그래밍\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"논리 프로그래머의 GUI를 여세요.\",\n    \"advancement.integrateddynamics.constant_definition\": \"상수 정의\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"상수 §6정수§7 변수 카드를 정의하세요.\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"덧셈\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"두 §e숫자§7를 더하고 결과를 표시하세요.\",\n    \"advancement.integrateddynamics.variable_materialization\": \"변수 구체화\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"§l구체화 장치§r에서 변수를 구체화 하세요.\",\n    \"advancement.integrateddynamics.variable_proxying\": \"변수 프록시\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"§l프록시§r에서 유효한 프록시 변수를 만드세요.\",\n    \"advancement.integrateddynamics.logical_list_building\": \"논리적으로 리스트 만들기\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"§6정수§7 1, 10, 100이 들어있는 정적인 리스트를 생성하세요.\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"아이템 원산지 식별\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"아이템의 모드 이름을 얻으세요.\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"나는 무엇을 보고 있을까?\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"당신이 보고 있는 블록을 얻으세요.\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"동석 덧셈\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"덧셈 연산자를 동적으로 호출하세요.\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"동적 리스트 필터링\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"정수 리스트 1, 10, 100 10으로 필터링 하세요.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"크리퍼 길들이기\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"디스플레이 패널에 크리퍼의 HP를 표시하세요.\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"스펀지 밟는 소리\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"젖은 스펀지를 밟는 소리를 재생하세요.\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"재귀적 재귀\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"무한한 리스트를 만들어서 디스플레이 패널에 표시하세요.\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"옵시디언에 마땅한 도구는?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"돌 곡괭이가 옵시디언을 부술 수 있는지 확인하고, 디스플레이 패널에 결과를 표시하세요.\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"스마트 감압판\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"문을 직접 가리키는 레드스톤 작성기를 사용하여 개체 이름 매칭을 기반으로 개체 판독기를 지나갈 때 자동으로 문을 여는 시스템을 만드세요. (힌트: 등호 연산자에 개체 이름을 첫 번째 인수로 전달하세요.)\",\n    \"valuetype.integrateddynamics.error.value\": \"유효하지 않은 값\",\n    \"valuetype.integrateddynamics.value_type\": \"유효한 값\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§e자료형: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§o값: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§e시그니처: §r\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"입력 \\\"%s\\\" (을)를 이 자료형으로 변환할 수 없습니다.\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"이 자료형은 입력 아이템이 필요합니다.\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"요소 %s 은(는) 유효하지 않음: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"이 리스트는 %s 자료형을 예상했지만 %s (이)가 주어졌습니다.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"연산자는 그 자리에 %s 자료형을 예상했지만, %s (이)가 주어졌습니다.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"연산자 %s는 자료형 (%s) (을)를 예상했지만, (%s) (이)가 주어졌습니다.\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"들어간 아이템이 블록이 아닙니다.\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"들어간 아이템이 액체를 포함하고 있지 않습니다.\",\n    \"valuetype.integrateddynamics.error.no_light_calculator\": \"자료형 %s (은)는 밝기 레벨에 쓰이지 않습니다.\",\n    \"valuetype.integrateddynamics.error.deserialize\": \"역직렬화 도중 문제가 발생 '%s': %s\",\n    \"valuetype.integrateddynamics.any.info\": \"어떤 타입의 자료형이라도 받아들입니다.\",\n    \"valuetype.integrateddynamics.number.info\": \"모든 숫자 자료형을 받아들입니다.\",\n    \"valuetype.integrateddynamics.named.info\": \"아이템과 블록의 표시되는 이름과 같은 모든 이름이 있는 것들을 받아들입니다.\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"아이템과 블록과 같은 고유한 이름이 있는 것들을 받아들입니다.\",\n    \"valuetype.integrateddynamics.ingredients.slot.info\": \"쉬프트 클릭하여 구성\",\n    \"parttype.integrateddynamics.error.low_energy\": \"에너지 부족\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"에너지 없음\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"비활성화\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"에러:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"기능: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"값: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"그룹: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"주의: 사용가능한 기능이 없습니다. 다른 모드가 설치되면 사용가능하게 될 수 있습니다.\",\n    \"parttype.integrateddynamics.audio_reader\": \"오디오 판독기\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"오디오 정보를 읽습니다.\",\n    \"parttype.integrateddynamics.block_reader\": \"블록 판독기\",\n    \"parttype.integrateddynamics.block_reader.info\": \"블록 정보를 읽습니다.\",\n    \"parttype.integrateddynamics.entity_reader\": \"개체 판독기\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"개체의 정보를 읽습니다.\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"추가 차원 판독기\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"모든 차원에 공유되는 정보를 읽습니다.\",\n    \"parttype.integrateddynamics.fluid_reader\": \"유체 판독기\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"유체 탱크를 읽습니다.\",\n    \"parttype.integrateddynamics.inventory_reader\": \"보관함 판독기\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"보관함에서 데이터를 읽습니다.\",\n    \"parttype.integrateddynamics.network_reader\": \"네트워크 판독기\",\n    \"parttype.integrateddynamics.network_reader.info\": \"대상 네트워크의 속성을 읽습니다.\",\n    \"parttype.integrateddynamics.machine_reader\": \"기계 판독기\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"기계의 속성을 읽습니다.\",\n    \"parttype.integrateddynamics.redstone_reader\": \"레드스톤 판독기\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"레드스톤 값을 읽습니다.\",\n    \"parttype.integrateddynamics.world_reader\": \"세계 판독기\",\n    \"parttype.integrateddynamics.world_reader.info\": \"세계, 블록 및 개체에 관련된 것을 읽습니다.\",\n    \"parttype.integrateddynamics.audio_writer\": \"오디오 작성기\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"오디오와 음악을 출력합니다.\",\n    \"parttype.integrateddynamics.effect_writer\": \"효과 작성기\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"입자를 출력합니다.\",\n    \"parttype.integrateddynamics.entity_writer\": \"개체 작성기\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"개체의 속성을 작성합니다.\",\n    \"parttype.integrateddynamics.machine_writer\": \"기계 작성기\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"기계의 속성을 작성합니다.\",\n    \"parttype.integrateddynamics.inventory_writer\": \"보관함 작성기\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"보관함에 작성합니다.\",\n    \"parttype.integrateddynamics.redstone_writer\": \"레드스톤 작성기\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"레드스톤에 작성합니다.\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"이 패널은 %s 을(를) 받지 않습니다.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"고정 밝기 패널\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"동적 밝기 패널\",\n    \"parttype.integrateddynamics.display_panel\": \"디스플레이 패널\",\n    \"parttype.integrateddynamics.display_panel.info\": \"변수 값을 시각적인 텍스트로 표시합니다.\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"단방향 커넥터\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"직선 거리에 있는 다른 단방향 커넥터와 연결할 수 있습니다.\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"전방향 커넥터\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"다른 차원을 포함하여 어디서나 다른 전방향 커넥터에 연결할 수 있습니다.\",\n    \"variable.integrateddynamics.error.invalid_item\": \"현재 변수는 이 측면에 쓰일 수 없습니다.\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"id가 %s인 부품을 현재 네트워크에서 찾을 수 없습니다.\",\n    \"variable.integrateddynamics.error.recursion\": \"id가 %s인 변수가 자기 자신을 참조하고 있습니다.\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§o프록시 ID: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"id가 %s인 프록시를 현재 네트워크에서 찾을 수 없습니다.\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"id가 %s인 프록시가 변수를 노출하고 있지 않습니다.\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"id가 %s인 프록시는 자료형이 %s인 변수를 노출하기를 예상했지만 %s 이(가) 발견되었습니다.\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§o지연기 ID: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"id가 %s인 지연기를 현재 네트워크에서 찾을 수 없습니다.\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"id가 %s인 지연기가 변수를 노출하고 있지 않습니다.\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"id가 %s인 프록시는 자료형이 %s인 변수를 노출하기를 예상했지만 %s 이(가) 발견되었습니다.\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§e기능: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§e자료형: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"부품 ID: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"이 기능은 %s를 예상했지만, %s 이(가) 주어졌습니다.\",\n    \"aspect.integrateddynamics.error.recursion\": \"기능 변수 %s 가 자기자신을 참조하고 있습니다.\",\n    \"aspect.integrateddynamics.error.no_value_interface\": \"유효한 값 인터페이스가 발견되지 않았습니다.\",\n    \"aspect.integrateddynamics.error.no_value_interface_value\": \"유효한 값 인터페이스의 값이 발견되지 않았습니다.\",\n    \"aspect.integrateddynamics.name\": \"기능\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp\": \"하프 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp.info\": \"하프 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum\": \"베이스 드럼 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum.info\": \"베이스 드럼 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"작은 북 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"작은 북 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat\": \"하이햇 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat.info\": \"하이햇 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass\": \"베이스 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass.info\": \"베이스 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute\": \"플루트 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute.info\": \"플루트 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell\": \"종 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell.info\": \"종 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar\": \"기타 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar.info\": \"기타 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime\": \"차임벨 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime.info\": \"차임벨 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone\": \"실로폰 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone.info\": \"실로폰 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone\": \"비브라폰 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone.info\": \"비브라폰 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell\": \"카우벨 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell.info\": \"카우벨 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo\": \"디저리두 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo.info\": \"디저리두 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit\": \"비트 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit.info\": \"비트 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo\": \"반조 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo.info\": \"반조 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling\": \"플링 음\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling.info\": \"플링 음을 읽습니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp\": \"하프 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp.info\": \"하프 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum\": \"베이스 드럼 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum.info\": \"베이스 드럼 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"작은 북 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"작은 북 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat\": \"하이햇 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat.info\": \"하이햇 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass\": \"베이스 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass.info\": \"베이스 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute\": \"플루트 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute.info\": \"플루트 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell\": \"종 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell.info\": \"종 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar\": \"기타 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar.info\": \"기타 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime\": \"차임벨 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime.info\": \"차임벨 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone\": \"실로폰 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone.info\": \"실로폰 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone\": \"비브라폰 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone.info\": \"비브라폰 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell\": \"카우벨 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell.info\": \"카우벨 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo\": \"디저리두 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo.info\": \"디저리투 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit\": \"비트 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit.info\": \"비트 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo\": \"반조 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo.info\": \"반조 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling\": \"플링 음\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling.info\": \"플링 음을 출력합니다. 예상 범위 [0, 24]\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"소리\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"주어진 소리의 이름\",\n    \"aspect.integrateddynamics.write.string.audio.text\": \"텍스트\",\n    \"aspect.integrateddynamics.write.string.audio.text.info\": \"주어진 텍스트를 음성으로 변환합니다\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager\": \"입자: 화난 주민\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble\": \"입자: 거품\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble.info\": \"특정 속도로 거품 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud\": \"입자: 구름\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud.info\": \"특정 속도로 구름 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit\": \"입자: 치명타\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit.info\": \"특정 속도로 치명타 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator\": \"입자: 피해 표시\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath\": \"입자: 드래곤의 숨결\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath.info\": \"특정 속도로 드래곤의 숨결 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava\": \"입자: 방울지는 용암\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava\": \"입자: 떨어지는 용암\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava.info\": \"특정 속도로 떨어지는 용암 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava\": \"입자: 착지한 용암\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water\": \"입자: 방울지는 물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water\": \"입자: 떨어지는 물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water.info\": \"특정 속도로 떨어지는 물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect\": \"입자: 효과\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect.info\": \"특정 속도로 효과 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian\": \"입자: 엔더 가디언\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit\": \"입자: 마법부여된 타격\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant\": \"입자: 마법부여된\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant.info\": \"특정 속도로 마법부여된 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod\": \"입자: 엔드 막대\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod.info\": \"특정 속도로 엔드 막대 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter\": \"입자: 폭발 방출기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter.info\": \"특정 속도로 폭발 방출기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion\": \"입자: 폭발\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework\": \"입자: 폭죽\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework.info\": \"특정 속도로 폭죽 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing\": \"입자: 낚시\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame\": \"입자: 화염\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame.info\": \"특정 속도로 화염 입자를방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame\": \"입자: 영혼불 화염\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame.info\": \"특정 속도로 영혼불 화염 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul\": \"입자: 영혼\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul.info\": \"특정 속도로 영혼 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash\": \"입자: 섬광\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash.info\": \"특정 속도로 섬광 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager\": \"입자: 행복한 주민\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter\": \"입자: 퇴비통\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter.info\": \"특정 속도로 퇴비통 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart\": \"입자: 하트\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart.info\": \"특정 속도로 하트 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect\": \"입자: 즉각적인 효과\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect.info\": \"특정 속도로 즉각적인 효과 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime\": \"입자: 슬라임\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime.info\": \"특정 속도로 슬라임 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball\": \"입자: 눈덩이\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball.info\": \"특정 속도로 눈덩이 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke\": \"입자: 큰 연기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke.info\": \"특정 속도로 큰 연기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava\": \"입자: 용암\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava.info\": \"특정 속도로 용암 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium\": \"입자: 균사체\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium.info\": \"특정 속도로 균사체 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note\": \"입자: 음표\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note.info\": \"특정 속도로 음표 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof\": \"입자: 펑\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof.info\": \"특정 속도로 펑 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal\": \"입자: 차원문\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal.info\": \"특정 속도로 차원문 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain\": \"입자: 비\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain.info\": \"특정 속도로 비 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke\": \"입자: 연기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke.info\": \"특정 속도로 연기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze\": \"입자: 재채기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze.info\": \"특정 속도로 재채기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit\": \"입자: 침\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit.info\": \"특정 속도로 침 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink\": \"입자: 오징어 먹물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink.info\": \"특정 속도로 오징어 먹물입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack\": \"입자: 휩쓸기 공격\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack.info\": \"특정 속도로 휩쓸기 공격 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying\": \"입자: 불사의 토템\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying.info\": \"특정 속도로 불사의 토템 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater\": \"입자: 수중\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash\": \"입자: 첨벙이는\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash.info\": \"특정 속도로 첨벙이는 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch\": \"입자: 마녀\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch.info\": \"특정 속도로 마녀 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop\": \"입자: 터지는 물방울\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop.info\": \"특정 속도로 터지는 물방울 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down\": \"입자: 하향 수류\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down.info\": \"특정 속도로 하향 수류 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up\": \"입자: 상향 수류\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up.info\": \"특정 속도로 상향 수류 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus\": \"입자: 노틸러스\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus.info\": \"특정 속도로 노틸러스 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin\": \"입자: 돌고래\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin.info\": \"특정 속도로 돌고래 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke\": \"입자: 아늑한 모닥불 연기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke.info\": \"특정 속도로 아늑한 모닥불 연기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke\": \"입자: 신호 모닥불 연기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke.info\": \"특정 속도로 신호 모닥불 연기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey\": \"입자: 방울지는 꿀\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey.info\": \"특정 속도로 방울지는 꿀 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey\": \"입자: 떨어지는 꿀\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey.info\": \"특정 속도로 떨어지는 꿀 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey\": \"입자: 착지한 꿀\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey.info\": \"특정 속도로 착지한 꿀 입자를 방출합니다\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar\": \"입자: 떨어지는 꽃가루\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar.info\": \"특정 속도로 떨어지는 꽃가루 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom\": \"입자: 떨어지는 포자꽃\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom.info\": \"특정 속도로 떨어지는 포자꽃 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash\": \"입자: 재\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash.info\": \"특정 속도로 재 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore\": \"입자: 진홍빛 포자\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore.info\": \"특정 속도로 진홍빛 포자 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore\": \"입자: 뒤틀린 포자\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore.info\": \"특정 속도로 뒤틀린 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air\": \"입자: 포자꽃 공기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air.info\": \"특정 속도로 포자꽃 공기입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear\": \"입자: 방울지는 흑요석 눈물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear.info\": \"특정 속도로 방울지는 흑요석 눈물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear\": \"입자: 떨어지는 흑요석 눈물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear.info\": \"특정 속도로 떨어지는 흑요석 눈물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear\": \"입자: 착지한 흑요석 눈물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear.info\": \"특정 속도로 착지한 흑요석 눈물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal\": \"입자: 역전된 차원문\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal.info\": \"특정 속도로 역전된 차원문 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash\": \"입자: 하얀색 재\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash.info\": \"특정 속도로 하얀색 재 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame\": \"입자: 작은 화염\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame.info\": \"특정 속도로 작은 화염 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake\": \"입자: 가루눈\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake.info\": \"특정 속도로 가루눈 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava\": \"입자: 방울지는 점적석 용암\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava.info\": \"특정 속도로 방울지는 점적석 용암 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava\": \"입자: 떨어지는 점적석 용암\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava.info\": \"특정 속도로 떨어지는 점적석 용암 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water\": \"입자: 방울지는 점적석 물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water.info\": \"특정 속도로 방울지는 점적석 물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water\": \"입자: 떨어지는 점적석 물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water.info\": \"특정 속도로 떨어지는 점적석 물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink\": \"입자: 발광 오징어 먹물\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink.info\": \"특정 속도로 발광 오징어 먹물 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow\": \"입자: 발광\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow.info\": \"특정 속도로 발광 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on\": \"입자: 밀랍칠하기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on.info\": \"특정 속도로 밀랍칠하기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off\": \"입자: 밀랍칠 없애기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off.info\": \"특정 속도로 밀랍칠없애기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark\": \"입자: 전기 스파크\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark.info\": \"특정 속도로 전기 스파크 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape\": \"입자: 긁기\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape.info\": \"특정 속도로 긁기 입자를 방출합니다.\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"블록 있음\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"대상 공간에 블록이 있는가\",\n    \"aspect.integrateddynamics.read.string.block.dimension\": \"차원\",\n    \"aspect.integrateddynamics.read.string.block.dimension.info\": \"대상 세계 차원\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"X 좌표\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"대상의 X 좌표\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Y 좌표\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"대상의 Y 좌표\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Z 좌표\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"대상의 Z 좌표\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"타일 개체 NBT\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"대상 타일 개체의 NBT\",\n    \"aspect.integrateddynamics.read.string.block.biome\": \"생물 군계\",\n    \"aspect.integrateddynamics.read.string.block.biome.info\": \"대상이 있는 생물 군계의 이름\",\n    \"aspect.integrateddynamics.read.integer.block.light\": \"밝기\",\n    \"aspect.integrateddynamics.read.integer.block.light.info\": \"대상이 있는 곳의 밝기\",\n    \"aspect.integrateddynamics.read.block.block\": \"블록\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"대상 블록\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"아이템 액자 회전\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"부착된 아이템 액자 안에 있는 아이템의 회전 수.\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"개체 리스트\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"대상 공간에 있는 개체 리스트\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"플레이어 리스트\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"이 세계에 있는 플레이어 리스트\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"개체\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"대상 공간 안에 선택된 개체\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"아이템 액자\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"판독기에 부착된 아이템 액자 안의 아이템\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"무작위\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"무작위 정수값\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"플레이어 수\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"서버에 있는 플레이어 수\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"틱 시간\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"나노초로 나타낸 서버의 평균 틱 시간\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"모든 세계의 평균 TPS\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"플레이어 리스트\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"이 서버에 있는 플레이어 리스트\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"탱크 비어있음\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"대상 유체 탱크가 비어있는가\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"탱크 가득 참\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"대상 유체 탱크가 가득 찼는가\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"탱크 비어있지 않음\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"대상 유체 탱크가 비어있지 않은가\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"탱크 여부\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"대상 블록이 탱크인가\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"탱크 수\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"대상에서 찾은 탱크에 수\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"유체 비율\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"활성화된 탱크 안의 유체의 양을 그 탱크의 용량으로 나눈 값\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"탱크 유체 리스트\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"모든 탱크에 들어있는 유체의 리스트\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"탱크 용량 리스트\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"모든 탱크의 용량의 리스트\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"탱크 유체\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"대상 탱크에 들어있는 유체\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"블록 유체\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"대상 블록에 들어있는 유체\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"인벤토리 가득 참\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"모든 슬록이 적어도 하나의 아이템을 가지고 있는가\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"인벤토리 비어있음\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"모든 슬록이 비어있는가\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"인벤토리 비어있지 않음\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"적어도 하나의 아이템이 있는가\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"인벤토리 여부\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"대상에 인벤토리가 있는가\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"인벤토리 수\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"아이템의 총량\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"슬롯 스택 크기\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"활성화된 슬롯의 스택 크기\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"슬롯 수\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"인벤토리 안의 슬롯 수\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"차 있는 슬롯 수\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"아이템을 가진 슬롯 수\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"아이템 비율\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"비어있지 않은 슬롯 수를 총 슬롯 수로 나눈 값\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"아이템 리스트\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"인벤토리 안의 아이템의 리스트\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"슬롯 아이템\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"활성화된 슬롯의 아이템\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"작업기 여부\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"대상이 작업을 하는 기계인가\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"작업 여부\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"대상이 진행 중인 작업이 있는가\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"작업 가능 여부\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"대상이 현재 상태에서 작업을 시작할 수 있는가\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"작업 중 여부\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"대상이 현재 작업 중인가\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"온도 여부\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"대상에 특정한 온도가 있는가\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"온도\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"최대 온도\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"최소 온도\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"기본 온도\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"레시피 여부\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"대상이 레시피를 다룰 수 있는가\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"레시피 리스트\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"대상이 다룰 수 있는 레시피의 리스트\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"입력에 대한 레시피의 출력\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"주어진 입력에 대한 레시피의 출력 결과를 반환하는 연산자.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"출력에 대한 레시피의 입력\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"주어진 출력에 대한 레시피의 가능한 입력을 반환하는 연산자.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"입력에 대한 레시피 리스트\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"주어진 입력으로 가능한 레시피의 리스트를 반환하는 연산자.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"출력에 대한 레시피 리스트\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"주어진 출력으로 가능한 레시피의 리스트를 반환하는 연산자.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"입력에 대한 레시피\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"주어진 입력에 대한 레시피를 반환하는 연산자.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"출력에 대한 레시피\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"주어진 출력에 대한 레시피를 반환하는 연산자.\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"네트워크 여부\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"대상이 네트워크를 가지고 있는가\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"요소 수\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"대상 네트워크의 요소 수\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"에너지 배터리 수\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"대상 네트워크의 에너지 배터리 수\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"에너지 소비 속도\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"대상 네트워크의 현재 틱 당 에너지 소비 속도\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"값\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"대상이 노출하고 있는 값\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"레드스톤 신호 높음\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"레드스톤 신호의 세기가 최대인가\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"레드스톤 신호 없음\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"레드스톤 신호가 없는가\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"레드스톤 신호 있음\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"레드스톤 신호가 있는가\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"레드스톤 시계\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"구성가능한 길이와 간격으로 펄스함.\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"레드스톤 신호 세기\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"정확한 레드스톤 신호의 세기\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"비교기\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"비교기의 값\",\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"레드스톤 신호\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"참이라면 레드스톤 신호 최대로 방출\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"레드스톤 펄스\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"만약 주어진 값이 변했다면 구성된 세기의 레드스톤 펄스를 방출.\",\n    \"aspect.integrateddynamics.write.integer.redstone\": \"레드스톤 신호\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"정확한 세기의 레드스톤 신호 방출\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"레드스톤 펄스\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"만약 주어진 값이 변했다면 구성된 세기의 레드스톤 펄스를 방출.\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"개체 여부\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"대상 공간에 개체가 있는가\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"몹 여부\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"대상 공간에 몹이 있는가\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"동물 여부\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"대상 공간에 동물이 있는가\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"플레이어 여부\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"대상 공간에 플레이어가 있는가\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"아이템 여부\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"대상 공간에 아이템이 있는가\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"날씨: 맑음\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"세계의 날씨가 맑은가\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"날씨: 비\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"날씨: 천둥\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"세계에 우뢰가 치고 있는가\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"낮\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"대상 세계가 낮인가\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"밤\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"대상 세계가 밤인가\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"개체 수\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"대상 공간에 있는 개체의 수\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"비 시간\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"이 세계에 비가 내릴 때까지 남은 틱\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"틱 시간\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"밀리초로 나타낸 이 세계의 평균 틱 시간\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"날짜\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"대상 세계의 날짜로 나타난 지나간 시간\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"밝기\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"대상의 밝기\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"플레이어 수\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"이 세계에 온라인인 플레이어의 수\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"이 세계의 TPS\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"시간\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"set time 명령어에 관련된 세계 시간\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"총 시간\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"set time 명령어에 관련 없는 세계 시간\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"세계 이름\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"대상 세계의 이름\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"플레이어 리스트\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"이 세계에 있는 플레이어의 리스트\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"탱크 ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"슬롯 ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"리스트 요소\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"범위\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"채널\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"펄스 간격\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"펄스 길이\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"펄스 시간 오프셋\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"볼륨\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"주파수\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"X 오프셋\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Y 오프셋\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Z 오프셋\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"입자 수\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"X 퍼짐\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Y 퍼짐\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Z 퍼짐\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"센 힘\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"일반적인 레드스톤 가루 대신 레드스톤 횃불을 흉내내야하는가\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"펄스 세기\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"방출되어야하는 펄스의 세기.\",\n    \"operator.integrateddynamics\": \"연산자\",\n    \"operator.integrateddynamics.applied.operator_name\": \"적용됨 %s [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"§e적용됨: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§e연산자: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§e카테고리: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§e변수 ID: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§e입력 자료형 %s: §r%s\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§e출력 자료형: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"연산자 %s이(가) 길이 %s의 입력을 받았지만 길이 %s이(가) 필요합니다.\",\n    \"operator.integrateddynamics.error.operator_param_wrong_input_length\": \"연산자에 입력 길이 %s이(가) 필요했지만 %s의 입력 길이는 %s 뿐입니다.\",\n    \"operator.integrateddynamics.error.null_type\": \"연산자 %s이(가) 위치 %s에서 null 변수를 입력으로 받았습니다.\",\n    \"operator.integrateddynamics.error.wrong_type\": \"연산자 %s은(는) 자료형 %s을(를) 예상했지만 위치 %s에서 자료형이 %s인 입력을 받았습니다.\",\n    \"operator.integrateddynamics.error.wrong_type_output\": \"연산자 %s이(가) 예상한 %s 대신 %s을(를) 반환했습니다.\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"연산자 %s은(는) 위치 %s에서 %s 자료형을 입력 받았지만 커링에 대해 %s 자료형이 예상됩니다.\",\n    \"operator.integrateddynamics.error.currying_overflow\": \"연산자 %s(%s 입력)을 %s 입력으로 커링하여 출력으로 연산자를 예상했지만 %s을(를) 얻었습니다.\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"연산자 %s은(는) 자료형 %s을(를) 받았지만 자료형 %s이(가) 예상됩니다.\",\n    \"operator.integrateddynamics.error.illegal_property\": \"이 속성은 %s을(를) 출력해야 하지만 %s을(를) 연산자 %s이(가) 찾았습니다.\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"ID가 %s인 변수를 현재 네트워크에서 찾을 수 없습니다.\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"ID가 %s인 변수가 자기 자신에 의존합니다. 무한 재귀입니다.\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"연산자 %s이(가) 연산자 %s을(를) 입력 길이 %s로 받았지만 길이 %s이(가) 필요합니다.\",\n    \"operator.integrateddynamics.error.operator_recursion_limit\": \"연산자 재귀 제한인 %s에 %s을(를) 호출할 때 도달했습니다. 게임을 충돌시키려고한거 맞죠?\",\n    \"operator.integrateddynamics.error.operator_nbt_path_expression\": \"잘못된 NBT 경로 경로 표현식을 찾았습니다. '%s': %s\",\n    \"operator.integrateddynamics.error.cast.no_mapping\": \" %s에서 %s로 가는 타입 캐스팅 매핑은 없습니다.\",\n    \"operator.integrateddynamics.error.cast.illegal\": \"%s에서 %s로의 캐스팅을 시도했지만 값이 \\\"%s\\\" 입니다.\",\n    \"operator.integrateddynamics.error.cast.unexpected\": \"%s 자료형의 값이 %s(으)로 캐스팅할 예상 자료형 %s과(와) 일치하지 않습니다.\",\n    \"operator.integrateddynamics.error.divide_by_zero\": \"0으로 나눌 수 없습니다.\",\n    \"operator.integrateddynamics.error.parse\": \"값 \\\"%s\\\" 은(는) %s로 파싱할 수 없습니다.\",\n    \"operator.integrateddynamics.error.no_derserializer\": \"연산자 값 \\\"%s\\\"을(를) 역직렬화하는 직렬기를 찾을 수 없습니다.\",\n    \"operator.integrateddynamics.error.regex.invalid\": \"잘못된 정규식: \\\"%s\\\"\",\n    \"operator.integrateddynamics.error.substring.index_negative\": \"부분 문자열의 인덱스는 음수가 될 수 없습니다.\",\n    \"operator.integrateddynamics.error.substring.longer_than_string\": \"부분 문자열의 인덱스는 문자열의 길이를 넘을 수 없습니다.\",\n    \"operator.integrateddynamics.error.group.index_negative\": \"그룹 인덱스는 음수가 될 수 없습니다.\",\n    \"operator.integrateddynamics.error.group.no_match\": \"정규식 \\\"%s\\\" 에 대한 그룹 정규식 일치를 \\\"%s\\\"에서 찾을 수 없습니다.\",\n    \"operator.integrateddynamics.error.group.no_match_group\": \"정규식 \\\"%s\\\" 에 대한 그룹 정규식 일치를 \\\"%s\\\"에서 그룹 \\\"%s\\\"에 대해 찾을 수 없습니다.\",\n    \"operator.integrateddynamics.error.regex_scan.index_negative\": \"정규식 스캔 인덱스는 음수가 될 수 없습니다.\",\n    \"operator.integrateddynamics.error.regex_scan.no_match_group\": \"정규식 \\\"%s\\\" 에 대한 그룹 스캔 일치를 \\\"%s\\\"에서 그룹 \\\"%s\\\"에 대해 찾을 수 없습니다.\",\n    \"operator.integrateddynamics.error.infinite_list.illegal\": \"연산자 %s는 무한한 리스트에 적용할 수 없습니다.\",\n    \"operator.integrateddynamics.error.index_out_of_bounds\": \"범위를 벗어난 인덱스. 요소 %s를 길이가 %s인 리스트에서 가져오려고 했습니다. getOrDefault 연산자를 사용하여 범위를 벗어났을 때 기본값을 가져오도록 하세요.\",\n    \"operator.integrateddynamics.error.slice.index_negative\": \"부분인덱스는 음수가 될 수 없습니다.\",\n    \"operator.integrateddynamics.error.reduce.empty\": \"reduce1 연산자가 빈 리스트의 머리를 가져오려고 시도했습니다. 대신에 reduce 연산자를 사용하여 빈 리스트에 대해 기본값을 제공하세요.\",\n    \"operator.integrateddynamics.error.operator_not_found\": \"이름이 \\\"%s\\\"인 연산자를 찾을 수 없습니다.\",\n    \"operator.integrateddynamics.logical\": \"논리\",\n    \"operator.integrateddynamics.logical.basename\": \"논리 %s\",\n    \"operator.integrateddynamics.arithmetic\": \"산수\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"산수 %s\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"덧셈\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"뺄셈\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"곱셈\",\n    \"operator.integrateddynamics.arithmetic.division\": \"나눗셈\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"최대\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"두 값 중 가장 큰 값을 가져옵니다.\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"최소\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"두 값 중 가장 작은 값을 가져옵니다.\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"계수\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"증가\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"감소\",\n    \"operator.integrateddynamics.integer.modulus\": \"계수\",\n    \"operator.integrateddynamics.integer.increment\": \"증가\",\n    \"operator.integrateddynamics.integer.decrement\": \"감소\",\n    \"operator.integrateddynamics.relational\": \"관계\",\n    \"operator.integrateddynamics.relational.basename\": \"관계 %s\",\n    \"operator.integrateddynamics.relational.equals\": \"같음\",\n    \"operator.integrateddynamics.relational.gt\": \"보다 큼\",\n    \"operator.integrateddynamics.relational.lt\": \"보다 작음\",\n    \"operator.integrateddynamics.relational.notequals\": \"같지 않음\",\n    \"operator.integrateddynamics.relational.ge\": \"크거나 같음\",\n    \"operator.integrateddynamics.relational.le\": \"작거나 같음\",\n    \"operator.integrateddynamics.binary\": \"이진수\",\n    \"operator.integrateddynamics.binary.basename\": \"이진수 %s\",\n    \"operator.integrateddynamics.binary.complement\": \"보수\",\n    \"operator.integrateddynamics.binary.rshift\": \"오른쪽 시프트\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"오른쪽으로 시프트하고 1이 채워집니다\",\n    \"operator.integrateddynamics.binary.lshift\": \"왼족 시프트\",\n    \"operator.integrateddynamics.binary.rzshift\": \"오른쪽 0 시프트\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"오른쪽으로 시프트하고 0이 채워집니다\",\n    \"operator.integrateddynamics.string.length\": \"길이\",\n    \"operator.integrateddynamics.string.length.info\": \"주어진 문자열의 길이\",\n    \"operator.integrateddynamics.string.concat\": \"연결\",\n    \"operator.integrateddynamics.string.concat.info\": \"주어진 두개의 문자열을 연결합니다\",\n    \"operator.integrateddynamics.string.contains\": \"문자열 포함\",\n    \"operator.integrateddynamics.string.contains.info\": \"주어진 부분 문자열이 주어진 문자열에 포함되는가\",\n    \"operator.integrateddynamics.string.contains_regex\": \"정규식 포함\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"주어진 정규경로 표현식이 주어진 문자열의 아무 부분 문자열과 일치하는가\",\n    \"operator.integrateddynamics.string.matches_regex\": \"정규식 일치\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"주어진 정규경로 표현식이 주어진 문자열과 일치하는가\",\n    \"operator.integrateddynamics.string.index_of\": \"인덱스 찾기\",\n    \"operator.integrateddynamics.string.index_of.info\": \"주어진 문자열에서 주어진 부분 문자열이 처음 일치하는 곳의 인덱스\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"정규식 인덱스 찾기\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"주어진 문자열의 부분 문자열에서 정규식 패턴과 처음 일치하는 곳의 인덱스\",\n    \"operator.integrateddynamics.string.starts_with\": \"시작 여부\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"주어진 문자열이 주어진 부분 문자열로 시작하는가\",\n    \"operator.integrateddynamics.string.ends_with\": \"끝 여부\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"주어진 문자열이 주어진 부분 문자열로 끝나는가.\",\n    \"operator.integrateddynamics.string.split_on\": \"분할\",\n    \"operator.integrateddynamics.string.split_on.info\": \"주어진 문자열을 주어진 (글자)구분자로 나눈 부분들을 담고 있는 리스트\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"분할 정규식\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"주어진 문자열을 주어진 정규식으로 나눈 부분들을 담고 있는 리스트\",\n    \"operator.integrateddynamics.string.substring\": \"부분 문자열\",\n    \"operator.integrateddynamics.string.substring.info\": \"주어진 문자열을 주어진 인덱스(포함)에서부터 주어진 인덱스(미포함)까지 자른 부분 문자열\",\n    \"operator.integrateddynamics.string.regex_group\": \"정규식 그룹\",\n    \"operator.integrateddynamics.string.regex_group.info\": \"주어진 문자열에서 주어진 정규식을 검색하여 주어진 인덱스에 있는 그룹과 일치하는 것을 반환합니다.\",\n    \"operator.integrateddynamics.string.regex_groups\": \"정규식 그룹 리스트\",\n    \"operator.integrateddynamics.string.regex_groups.info\": \"주어진 문자열에서 주어진 정규식을 검색하여 주어진 인덱스에 있는 모든 그룹과 일치하는 것들의 리스트를 반환합니다.\",\n    \"operator.integrateddynamics.string.regex_scan\": \"정규식 스캔\",\n    \"operator.integrateddynamics.string.regex_scan.info\": \"주어진 문자열에서 주어진 정규식과 일치하는 모든 것들을 검색하고 주어진 인덱스에 있는 그룹을 반환합니다.\",\n    \"operator.integrateddynamics.string.replace\": \"교체\",\n    \"operator.integrateddynamics.string.replace.info\": \"주어진 문자열에서 검색어와 일치하는 모든 사례들을 찾아 주어진 문자열로 교체합니다.\",\n    \"operator.integrateddynamics.string.replace_regex\": \"교체 정규식\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"주어진 문자열에서 검색 패턴과 일치하는 모든 사례들을 찾아 주어진 문자열로 교체합니다.\",\n    \"operator.integrateddynamics.string.join\": \"접합\",\n    \"operator.integrateddynamics.string.join.info\": \"주어진 문자열의 리스트를 주어진 구분자로 접합한 새로운 문자열을 생성합니다.\",\n    \"operator.integrateddynamics.string.name\": \"문자열 변환\",\n    \"operator.integrateddynamics.string.name.info\": \"문자열이나 이름있는 오브젝트의 이름을 얻거나 그것을 문자열로 변환합니다.\",\n    \"operator.integrateddynamics.string.unique_name\": \"고유 이름\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"오브젝트의 고유 이름을 가져옵니다.\",\n    \"operator.integrateddynamics.number.round\": \"반올림\",\n    \"operator.integrateddynamics.number.round.info\": \"가장 가까운 정수로 반올림\",\n    \"operator.integrateddynamics.number.ceil\": \"올림\",\n    \"operator.integrateddynamics.number.ceil.info\": \"가장 가까운 정수로 올림\",\n    \"operator.integrateddynamics.number.floor\": \"내림\",\n    \"operator.integrateddynamics.number.floor.info\": \"가장 가까운 정수로 내림\",\n    \"operator.integrateddynamics.list.length\": \"길이\",\n    \"operator.integrateddynamics.list.length.info\": \"주어진 리스트의 길이\",\n    \"operator.integrateddynamics.list.empty\": \"비어있음\",\n    \"operator.integrateddynamics.list.empty.info\": \"리스트가 비었는가\",\n    \"operator.integrateddynamics.list.notempty\": \"비어있지 않음\",\n    \"operator.integrateddynamics.list.notempty.info\": \"리스트가 비어있지 않은가\",\n    \"operator.integrateddynamics.list.get\": \"가져오기\",\n    \"operator.integrateddynamics.list.get.info\": \"주어진 위치에 있는 리스트의 요소를 가져옵니다. 인덱스가 범위를 벗어났다면 오류가 발생합니다.\",\n    \"operator.integrateddynamics.list.get_or_default\": \"가져오기 혹은 기본값\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"주어진 위치에 있는 리스트의 요소를 안전하게 가져옵니다. 요소를 사용할 수 없다면 기본값을 가져옵니다.\",\n    \"operator.integrateddynamics.list.contains\": \"포함\",\n    \"operator.integrateddynamics.list.contains.info\": \"리스트가 주어진 요소를 포함하는가.\",\n    \"operator.integrateddynamics.list.contains_p\": \"술어 포함\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"리스트의 요소 중 하나 이상이 주어진 술어에 대해 참을 반환하는가.\",\n    \"operator.integrateddynamics.list.count\": \"개수\",\n    \"operator.integrateddynamics.list.count.info\": \"리스트 안에 있는 주어진 요소의 개수\",\n    \"operator.integrateddynamics.list.count_p\": \"술어 개수\",\n    \"operator.integrateddynamics.list.count_p.info\": \"리스트에서의 요소 중 주어진 술어에 대해 참을 반환하는 것의 개수\",\n    \"operator.integrateddynamics.list.append\": \"추가\",\n    \"operator.integrateddynamics.list.append.info\": \"주어진 리스트에 주어진 항목을 추가합니다.\",\n    \"operator.integrateddynamics.list.concat\": \"연결\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"느긋한 리스트 작성기\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"첫번째 값과 다음값을 만들기 위해 이전값에 적용될 연산자를 받아 느긋하게 리스트를 작성합니다.\",\n    \"operator.integrateddynamics.list.head\": \"머리\",\n    \"operator.integrateddynamics.list.head.info\": \"주어진 리스트의 첫번째 요소를 가져옵니다.\",\n    \"operator.integrateddynamics.list.tail\": \"꼬리\",\n    \"operator.integrateddynamics.list.tail.info\": \"주어진 리스트의 마지막 요소를 제거합니다.\",\n    \"operator.integrateddynamics.list.uniq_p\": \"고유 술어\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"주어진 비교 술어를 기반으로 리스트의 모든 요소에서 중복을 제거합니다.\",\n    \"operator.integrateddynamics.list.uniq\": \"고유\",\n    \"operator.integrateddynamics.list.uniq.info\": \"리스트의 모든 요소에서 중복을 제거합니다.\",\n    \"operator.integrateddynamics.list.slice\": \"분할\",\n    \"operator.integrateddynamics.list.slice.info\": \"주어진 리스트를 주어진 인덱스(포함)에서부터 주어진 인덱스(미포함)까지 자른 부분 집합을 가져옵니다.\",\n    \"operator.integrateddynamics.block\": \"블록\",\n    \"operator.integrateddynamics.block.basename\": \"블록 %s\",\n    \"operator.integrateddynamics.block.opaque\": \"불투명\",\n    \"operator.integrateddynamics.block.opaque.info\": \"주어진 블록이 불투명한가\",\n    \"operator.integrateddynamics.block.itemstack\": \"아이템\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"주어진 블록의 아이템\",\n    \"operator.integrateddynamics.block.mod\": \"모드\",\n    \"operator.integrateddynamics.block.mod.info\": \"주어진 블록의 모드의 이름\",\n    \"operator.integrateddynamics.block.breaksound\": \"블록 부수는 소리\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"주어진 블록을 부수는 소리\",\n    \"operator.integrateddynamics.block.placesound\": \"블록 놓는 소리\",\n    \"operator.integrateddynamics.block.placesound.info\": \"주어진 블록을 놓는 소리\",\n    \"operator.integrateddynamics.block.stepsound\": \"블록 밟는 소리\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"주어진 블록을 밟는 소리\",\n    \"operator.integrateddynamics.block.isshearable\": \"깎을 수 있는 블록\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"주어진 블록이 깎을 수 있는가\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"블록 식물을 유지할 수 있는 블록\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"주어진 블록이 주어진 블록 식물을 유지할 수 있는가\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"아이템 식물을 유지할 수 있는 블록\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"주어진 블록이 주어진 아이템 식물을 유지할 수 있는가\",\n    \"operator.integrateddynamics.block.plantage\": \"블록 식물 나이\",\n    \"operator.integrateddynamics.block.plantage.info\": \"주어진 식물 블록의 나이\",\n    \"operator.integrateddynamics.block.blockbyname\": \"이름에 대한 블록\",\n    \"operator.integrateddynamics.block.blockproperties\": \"블록 속성\",\n    \"operator.integrateddynamics.block.blockproperties.info\": \"블록의 속성을 NBT 복합 태그로 가져옵니다.\",\n    \"operator.integrateddynamics.block.blockfromproperties\": \"블록 속성들\",\n    \"operator.integrateddynamics.block.blockfromproperties.info\": \"주어진 속성이 적용된 주어진 블록을 가져옵니다.\",\n    \"operator.integrateddynamics.block.blockpossibleproperties\": \"가능한 블록 속성 리스트\",\n    \"operator.integrateddynamics.itemstack\": \"아이템\",\n    \"operator.integrateddynamics.itemstack.basename\": \"아이템 %s\",\n    \"operator.integrateddynamics.itemstack.size\": \"크기\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"현재 스택 크기\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"최대 크기\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"최대 스택 크기\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"스택 가능 여부\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"적어도 두 개를 스택으로 들 수 있는가.\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"손상 가능 여부\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"아이템이 손상될 수 있는가.\",\n    \"operator.integrateddynamics.itemstack.damage\": \"손상\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"현재 아이템 손상\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"최대 손상\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"최대 아이템 손상\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"마법부여 여부\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"아이템에 마법이 부여되어있는가\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"마법부여 가능 여부\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"아이템에 마법을 부여할 수 있는가\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"수리 비용\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"아이템의 수리 비용\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"희귀도\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"아이템의 희귀도\",\n    \"operator.integrateddynamics.itemstack.strength\": \"블록에 대한 힘\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"주어진 블록에 대한 이 아이템의 힘\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"채굴 가능 여부\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"아이템이 주어진 블록을 채굴할 수 있는가\",\n    \"operator.integrateddynamics.itemstack.block\": \"블록\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"주어진 아이템에 대한 블록\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"유체 포함 여부\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"주어진 아이템이 유체를 가지고 있는가\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"유체\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"주어진 아이템에 대한 유체\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"유체 용량\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"mB로 나타낸 주어진 아이템의 유체 용량\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"NBT 같음\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"주어진 아이템들의 NBT가 같은가\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt\": \"NBT 없는 원시 아이템 같음\",\n    \"operator.integrateddynamics.itemstack.israwitemequal\": \"원시 아이템 같음\",\n    \"operator.integrateddynamics.itemstack.mod\": \"모드\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"주어진 아이템의 모드의 이름\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"연료 연소 시간\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"틱으로 나타낸 주어진 아이템의 연소 시간\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"연료 여부\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"주어진 아이템을 연료로 쓸 수 있는가\",\n    \"operator.integrateddynamics.itemstack.tag.info\": \"주어진 아이템의 태그 이름(문자열)\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"인벤토리를 가진 아이템\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"주어진 아이템이 인벤토리를 가지고 있는가\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"아이템 인벤토리 크기\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"주어진 아이템 핸들러의 인벤토리 크기를 검색합니다.\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"아이템 인벤토리\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"주어진 아이템 핸들러의 인벤토리를 검색합니다.\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"아이템 스택 크기\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"주어진 아이템의 스택 크기를 설정합니다.\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"이름에 대한 아이템\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"리스트 아이템 수\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"리스트에서 주어진 아이템의 정확한 총 아이템 수를 가져옵니다.\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"아이템 NBT\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"주어진 아이템의 NBT 태그를 가져옵니다.\",\n    \"operator.integrateddynamics.itemstack.hasnbt\": \"NBT 여부\",\n    \"operator.integrateddynamics.itemstack.hasnbt.info\": \"주어진 아이템 스택이 NBT 태그를 가지고 있는가.\",\n    \"operator.integrateddynamics.entity\": \"개체\",\n    \"operator.integrateddynamics.entity.basename\": \"개체 %s\",\n    \"operator.integrateddynamics.entity.ismob\": \"몹 여부\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"개체가 몹인가\",\n    \"operator.integrateddynamics.entity.isanimal\": \"동물 여부\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"주어진 개체가 동물인가\",\n    \"operator.integrateddynamics.entity.isitem\": \"아이템 여부\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"주어진 개체가 아이템인가\",\n    \"operator.integrateddynamics.entity.isplayer\": \"플레이어 여부\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"주어진 개체가 플레이어인가\",\n    \"operator.integrateddynamics.entity.isminecart\": \"광산 수레 여부\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"주어진 개체가 광산 수레인가\",\n    \"operator.integrateddynamics.entity.item\": \"아이템\",\n    \"operator.integrateddynamics.entity.item.info\": \"아이템 개체에 대한 아이템\",\n    \"operator.integrateddynamics.entity.health\": \"체력\",\n    \"operator.integrateddynamics.entity.health.info\": \"개체의 체력\",\n    \"operator.integrateddynamics.entity.width\": \"너비\",\n    \"operator.integrateddynamics.entity.width.info\": \"개체의 너비\",\n    \"operator.integrateddynamics.entity.height\": \"높이\",\n    \"operator.integrateddynamics.entity.height.info\": \"개체의 높이\",\n    \"operator.integrateddynamics.entity.isburning\": \"불이 붙었는지 여부\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"개체에 불이 붙었는가\",\n    \"operator.integrateddynamics.entity.iswet\": \"젖었는지 여부\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"개체가 젖었는가\",\n    \"operator.integrateddynamics.entity.iscrouching\": \"웅크리기 여부\",\n    \"operator.integrateddynamics.entity.iscrouching.info\": \"개체가 웅크리고 있는가\",\n    \"operator.integrateddynamics.entity.iseating\": \"먹는지 여부\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"개체가 먹고 있는가\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"방어구 인벤토리\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"개체가 방어구로 입고있는 아이템의 목록\",\n    \"operator.integrateddynamics.entity.inventory\": \"플레이어 인벤토리\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"플레이어가 들고있는 아이템의 목록.\",\n    \"operator.integrateddynamics.entity.mod\": \"모드\",\n    \"operator.integrateddynamics.entity.mod.info\": \"주어진 개체의 모드의 이름\",\n    \"operator.integrateddynamics.entity.targetblock\": \"대상 블록\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"주어진 개체가 현재 보고 있는 블록.\",\n    \"operator.integrateddynamics.entity.targetentity\": \"대상 개체\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"주어진 개체가 현재 보고 있는 개체.\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"GUI 열고 있는지 여부\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"주어진 플레이어가 외부 GUI를 열고 있는가.\",\n    \"operator.integrateddynamics.entity.helditem\": \"주요 손에 든 아이템\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"주어진 개체가 현재 주요 손에 들고 있는 아이템.\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"보조 손에 든 아이템\",\n    \"operator.integrateddynamics.entity.mounted\": \"탑승한 개체 리스트\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"주어진 개체에 현재 탑승하고 있는 개체 리스트.\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"아이템 액자 내용물\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"주어진 아이템 액자의 내용물.\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"아이템 액자 회전\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"주어진 아이템 액자의 회전.\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"개체 맞는 소리\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"주어진 개체가 맞는 소리.\",\n    \"operator.integrateddynamics.entity.deathsound\": \"개체 죽는 소리\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"주어진 개체가 죽는 소리.\",\n    \"operator.integrateddynamics.entity.age\": \"개체 나이\",\n    \"operator.integrateddynamics.entity.age.info\": \"주어진 개체의 나이.\",\n    \"operator.integrateddynamics.entity.ischild\": \"개체 아기 여부\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"주어진 개체가 아기인가.\",\n    \"operator.integrateddynamics.entity.canbreed\": \"번식 가능 여부\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"주어진 개체가 번식할 준비가 되었는가.\",\n    \"operator.integrateddynamics.entity.isinlove\": \"사랑에 빠졌는지 여부\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"주어진 개체가 사랑에 빠져서 번식할 준비가 되었는가.\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"번식 가능 아이템 여부\",\n    \"operator.integrateddynamics.entity.isshearable\": \"개체 깎을 수 있는지 여부\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"주어진 개체를 깎을 수 있는가\",\n    \"operator.integrateddynamics.entity.nbt\": \"개체 NBT\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"주어진 개체의 NBT.\",\n    \"operator.integrateddynamics.entity.entitytype\": \"개체 유형\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"개체 유형 이름.\",\n    \"operator.integrateddynamics.entity.entityitems\": \"개체 아이템 리스트\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"이 개체에 들어있는 아이템의 리스트.\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"개체 유체\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"이 개체에 들어있는 유체.\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"개체에 저장된 에너지\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"이 개체에 저장된 에너지의 양\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"개체 에너지 용량\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"이 개체의 에너지 용량.\",\n    \"operator.integrateddynamics.fluidstack\": \"유체\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"유체 %s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"양\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"mB로 나타낸 양\",\n    \"operator.integrateddynamics.fluidstack.block\": \"블록\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"유체의 블록\",\n    \"operator.integrateddynamics.fluidstack.density\": \"밀도\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"유체의 밀도\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"점도\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"유체의 점도\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"희귀도\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"유체의 희귀도\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal\": \"원시 유체 같음\",\n    \"operator.integrateddynamics.fluidstack.mod\": \"모드\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"주어진 유체의 모드의 이름\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"유체 NBT\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"주어진 유체의 NBT 태그를 가져옵니다.\",\n    \"operator.integrateddynamics.fluidstack.with_amount\": \"양만큼의 유체\",\n    \"operator.integrateddynamics.fluidstack.with_amount.info\": \"주어진 유체를 주어진 양만큼 복사해옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_size\": \"NBT 복합 크기\",\n    \"operator.integrateddynamics.nbt.compound_size.info\": \"주어진 NBT 복합 태그 내의 항목 수\",\n    \"operator.integrateddynamics.nbt.compound_keys\": \"NBT 복합 키 리스트\",\n    \"operator.integrateddynamics.nbt.compound_keys.info\": \"주어진 NBT 복합 태그 내부의 키의 리스트\",\n    \"operator.integrateddynamics.nbt.compound_haskey\": \"NBT 복합 키 여부\",\n    \"operator.integrateddynamics.nbt.compound_haskey.info\": \"주어진 NBT 복합 태그가 주어진 키를 가지고 있는가\",\n    \"operator.integrateddynamics.nbt.compound_type\": \"NBT 복합 항목 자료형\",\n    \"operator.integrateddynamics.nbt.compound_type.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 자료형\",\n    \"operator.integrateddynamics.nbt.compound_value_tag\": \"NBT 복합 값\",\n    \"operator.integrateddynamics.nbt.compound_value_tag.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 모든 자료형의 값\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean\": \"NBT 복합 값 Boolean\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 Boolean 값\",\n    \"operator.integrateddynamics.nbt.compound_value_integer\": \"NBT 복합 값 Integer\",\n    \"operator.integrateddynamics.nbt.compound_value_integer.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 정수 값\",\n    \"operator.integrateddynamics.nbt.compound_value_long\": \"NBT 복합 값 Long\",\n    \"operator.integrateddynamics.nbt.compound_value_long.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 Long 값\",\n    \"operator.integrateddynamics.nbt.compound_value_double\": \"NBT 복합 값 Double\",\n    \"operator.integrateddynamics.nbt.compound_value_double.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 Double 값\",\n    \"operator.integrateddynamics.nbt.compound_value_string\": \"NBT 복합 값 String\",\n    \"operator.integrateddynamics.nbt.compound_value_string.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 문자열 값\",\n    \"operator.integrateddynamics.nbt.compound_value_compound\": \"NBT 복합 값 복합체\",\n    \"operator.integrateddynamics.nbt.compound_value_compound.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 복합 값\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag\": \"NBT 복합 값 List NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag.info\": \"주어진 NBT 복합 태그의 주어진 키에 해당하는 NBT 목록 값\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte\": \"NBT 복합 값 Byte Array\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte.info\": \"Integer 리스트로 나타낸 주어진 NBT 복합 태그의 주어진 키에 해당하는 Byte 배열\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int\": \"NBT 복합 값 Integer Array\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int.info\": \"Integer 리스트로 나타낸 주어진 NBT 복합 태그의 주어진 키에 해당하는 Integer 배열\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long\": \"NBT 복합 값 Long Array\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long.info\": \"Long 리스트로 나타낸 주어진 NBT 복합 태그의 주어진 키에 해당하는 Long 배열\",\n    \"operator.integrateddynamics.nbt.compound_without\": \"NBT 복합 제외\",\n    \"operator.integrateddynamics.nbt.compound_without.info\": \"주어진 키가 없는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean\": \"NBT 복합 Boolean 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean.info\": \"주어진 Boolean 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_short\": \"NBT 복합 Short 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_short.info\": \"short로 간주한 주어진 Integer 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_integer\": \"NBT 복합 Integer 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_integer.info\": \"주어진 Integer 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_long\": \"NBT 복합 Long 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_long.info\": \"주어진 Long 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_double\": \"NBT 복합 Double 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_double.info\": \"주어진 Double 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_float\": \"NBT 복합 Float 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_float.info\": \"float으로 간주한 주어진 Double 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_string\": \"NBT 복합 String 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_string.info\": \"주어진 문자열 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_tag\": \"NBT 복합 NBT 포함\",\n    \"operator.integrateddynamics.nbt.compound_with_tag.info\": \"주어진 NBT 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag\": \"NBT 복합 NBT 포함 리스트\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag.info\": \"주어진 NBT 리스트 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte\": \"NBT 복합 Byte 포함 리스트\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte.info\": \"BNT Byte 배열 항목으로 간주한 주어진 Integer 리스트가 있는 지정된 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int\": \"NBT 복합 Integer 포함 리스트\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int.info\": \"주어진 NBT Integer 배열 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long\": \"NBT 복합 Long 포함 리스트\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long.info\": \"주어진 NBT Long 배열 항목이 있는 주어진 NBT 복합 태그의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.compound_subset\": \"NBT 복합 부분집합\",\n    \"operator.integrateddynamics.nbt.compound_subset.info\": \"첫번째 NBT 복합 태그가 두번째 NBT 복합 태그의 부분 집합인가. 이것은 중첩된 태그를 재귀적으로 확인합니다.\",\n    \"operator.integrateddynamics.nbt.compound_union\": \"NBT 복합 합집합\",\n    \"operator.integrateddynamics.nbt.compound_intersection\": \"NBT 복합 교집합\",\n    \"operator.integrateddynamics.nbt.compound_minus\": \"NBT 복합 차집합\",\n    \"operator.integrateddynamics.nbt.as_boolean\": \"NBT Boolean을 Boolean으로\",\n    \"operator.integrateddynamics.nbt.as_boolean.info\": \"주어진 NBT Boolean 태그의 Boolean 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_byte\": \"NBT Byte를 Integer로\",\n    \"operator.integrateddynamics.nbt.as_byte.info\": \"주어진 NBT Byte 태그의 Integer 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_short\": \"NBT Short를 Integer로\",\n    \"operator.integrateddynamics.nbt.as_short.info\": \"주어진 NBT Short 태그의 Integer 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_int\": \"NBT Integer를 Integer로\",\n    \"operator.integrateddynamics.nbt.as_int.info\": \"주어진 NBT Integer 태그의 Integer 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_long\": \"NBT Long을 Long으로\",\n    \"operator.integrateddynamics.nbt.as_long.info\": \"주어진 NBT Long 태그의 Long 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_double\": \"NBT Double을 Double로\",\n    \"operator.integrateddynamics.nbt.as_double.info\": \"주어진 NBT Double 태그의 Double 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_float\": \"NBT Float을 Double로\",\n    \"operator.integrateddynamics.nbt.as_float.info\": \"주어진 NBT Float 태그의 Double 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_string\": \"NBT String을 String으로\",\n    \"operator.integrateddynamics.nbt.as_string.info\": \"주어진 NBT String 태그의 String 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_tag_list\": \"NBT List를 NBT List로\",\n    \"operator.integrateddynamics.nbt.as_tag_list.info\": \"주어진 NBT 리스트 태그의 NBT 리스트 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_byte_list\": \"NBT Byte 배열을 Byte List로\",\n    \"operator.integrateddynamics.nbt.as_byte_list.info\": \"주어진 NBT Byte 배열 태그의 Byte 리스트 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_int_list\": \"NBT Integer 배열을 Integer List로\",\n    \"operator.integrateddynamics.nbt.as_int_list.info\": \"주어진 NBT Integer 배열 태그의 Integer 리스트 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.as_long_list\": \"NBT Long 배열을 Long List로\",\n    \"operator.integrateddynamics.nbt.as_long_list.info\": \"주어진 NBT Long 배열 태그의 Long List 값을 가져옵니다.\",\n    \"operator.integrateddynamics.nbt.from_boolean\": \"NBT Byte를 Boolean에서\",\n    \"operator.integrateddynamics.nbt.from_boolean.info\": \"주어진 Boolean 값에서 NBT Byte 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_short\": \"NBT Short를 Integer에서\",\n    \"operator.integrateddynamics.nbt.from_short.info\": \"주어진 Integer 값에서 NBT Short 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_byte\": \"NBT Byte를 Integer에서\",\n    \"operator.integrateddynamics.nbt.from_byte.info\": \"주어진 Integer 값에서 NBT Byte 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_int\": \"NBT Integer를 Integer에서\",\n    \"operator.integrateddynamics.nbt.from_int.info\": \"주어진 Integer 값에서 NBT Integer 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_long\": \"NBT Long을 Long에서\",\n    \"operator.integrateddynamics.nbt.from_long.info\": \"주어진 Long 값에서 NBT Long 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_double\": \"NBT Double을 Double에서\",\n    \"operator.integrateddynamics.nbt.from_double.info\": \"주어진 Double 값에서 NBT Double 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_float\": \"NBT Float를 Double에서\",\n    \"operator.integrateddynamics.nbt.from_string\": \"NBT String을 String에서\",\n    \"operator.integrateddynamics.nbt.from_string.info\": \"주어진 문자열 값에서 NBT 문자열 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_tag_list\": \"NBT List를 Tag List에서\",\n    \"operator.integrateddynamics.nbt.from_tag_list.info\": \"주어진 NBT 리스트 값에서 NBT 리스트 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_byte_list\": \"NBT Byte 배열을 Byte List에서\",\n    \"operator.integrateddynamics.nbt.from_byte_list.info\": \"주어진 Integer 리스트 값에서 NBT Byte 배열 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_int_list\": \"NBT Integer 배열을 Integer List에서\",\n    \"operator.integrateddynamics.nbt.from_int_list.info\": \"주어진 Integer 리스트 값에서 NBT Integer 배열 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.from_long_list\": \"NBT Long 배열을 Long List에서\",\n    \"operator.integrateddynamics.nbt.from_long_list.info\": \"주어진 Long List 값에서 NBT Long 배열 태그를 만듭니다.\",\n    \"operator.integrateddynamics.nbt.path_match_first\": \"첫번째로 일치하는 NBT 경로\",\n    \"operator.integrateddynamics.nbt.path_match_first.info\": \"주어진 NBT 값에 주어진 NBT 경로 표현식을 적용하고 첫번째로 일치하는 것을 반환합니다.\",\n    \"operator.integrateddynamics.nbt.path_match_all\": \"모든 일치하는 NBT 경로\",\n    \"operator.integrateddynamics.nbt.path_match_all.info\": \"주어진 NBT 값에 주어진 NBT 경로 표현식을 적용하고 일치하는 모든 것을 리스트로 반환합니다.\",\n    \"operator.integrateddynamics.nbt.path_test\": \"NBT 경로 테스트\",\n    \"operator.integrateddynamics.nbt.path_test.info\": \"주어진 NBT 경로 표현식이 주어진 NBT 값과 일치하는지 테스트합니다.\",\n    \"operator.integrateddynamics.operator\": \"연산자\",\n    \"operator.integrateddynamics.operator.basename\": \"연산자 %s\",\n    \"operator.integrateddynamics.operator.apply\": \"적용\",\n    \"operator.integrateddynamics.operator.apply.info\": \"주어진 값을 주어진 연산자에 적용합니다.\",\n    \"operator.integrateddynamics.operator.apply2\": \"적용 2\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"주어진 두 값을 주어진 연산자에 적용합니다.\",\n    \"operator.integrateddynamics.operator.apply3\": \"적용 3\",\n    \"operator.integrateddynamics.operator.map\": \"매핑\",\n    \"operator.integrateddynamics.operator.map.info\": \"리스트의 모든 요소에 주어진 연산자를 적용하여 새로운 리스트에 결과를 매핑합니다.\",\n    \"operator.integrateddynamics.operator.filter\": \"필터\",\n    \"operator.integrateddynamics.operator.filter.info\": \"리스트의 요소를 주어진 술어로 모두 필터링합니다.\",\n    \"operator.integrateddynamics.operator.conjunction\": \"논리곱\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"두 술어의 논리곱을 취합니다.\",\n    \"operator.integrateddynamics.operator.disjunction\": \"논리합\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"두 술어의 논리합를 취합니다.\",\n    \"operator.integrateddynamics.operator.negation\": \"부정\",\n    \"operator.integrateddynamics.operator.negation.info\": \"술어의 부정을 취합니다.\",\n    \"operator.integrateddynamics.operator.pipe\": \"파이프\",\n    \"operator.integrateddynamics.operator.pipe.info\": \"첫번째 연산자와 두번째 연산자를 연결한 새로운 연산자를 만듭니다.\",\n    \"operator.integrateddynamics.operator.pipe2\": \"파이프 2\",\n    \"operator.integrateddynamics.operator.pipe2.info\": \"입력을 첫번째 연산자와 두번째 연산자에 전달하여 그 출력을 둘 다 세번째 연산자에 연결한 새로운 연산자를 만듭니다.\",\n    \"operator.integrateddynamics.operator.flip\": \"뒤집기\",\n    \"operator.integrateddynamics.operator.flip.info\": \"연산자의 처음 두 매개변수를 뒤집습니다.\",\n    \"operator.integrateddynamics.virtual.flipped\": \"뒤집어짐\",\n    \"operator.integrateddynamics.operator.reduce\": \"감소\",\n    \"operator.integrateddynamics.operator.reduce.info\": \"주어진 연산자를 리스트의 모든 요소에 적용하여 하나의 값으로 줄입니다.\",\n    \"operator.integrateddynamics.operator.reduce1\": \"감소 1\",\n    \"operator.integrateddynamics.operator.reduce1.info\": \"주어진 연산자를 리스트의 모든 요소에 적용하여 하나의 값으로 줄입니다. 감소1(연산자, 리스트)는 (연산자, 꼬리(리스트), 머리(리스트))와 같습니다.\",\n    \"operator.integrateddynamics.operator.by_name\": \"이름에 대한 연산자\",\n    \"operator.integrateddynamics.operator.by_name.info\": \"주어진 고유 이름을 가진 연산자를 가져옵니다.\",\n    \"operator.integrateddynamics.virtual.recipeoutputbyinput\": \"입력에 대한 레시피의 출력\",\n    \"operator.integrateddynamics.virtual.recipeinputsbyoutput\": \"출력에 대한 레시피의 입력\",\n    \"operator.integrateddynamics.virtual.recipesbyinput\": \"입력에 대한 레시피 리스트\",\n    \"operator.integrateddynamics.virtual.recipesbyoutput\": \"출력에 대한 레시피 리스트\",\n    \"operator.integrateddynamics.ingredients\": \"재료\",\n    \"operator.integrateddynamics.ingredients.basename\": \"재료 %s\",\n    \"operator.integrateddynamics.ingredients.items\": \"재료 아이템\",\n    \"operator.integrateddynamics.ingredients.items.info\": \"아이템의 리스트\",\n    \"operator.integrateddynamics.ingredients.fluids\": \"재료 유체\",\n    \"operator.integrateddynamics.ingredients.fluids.info\": \"유체의 리스트\",\n    \"operator.integrateddynamics.ingredients.energies\": \"재료 에너지 요소\",\n    \"operator.integrateddynamics.ingredients.energies.info\": \"에너지 요소의 리스트\",\n    \"operator.integrateddynamics.ingredients.with_item\": \"아이템으로 재료\",\n    \"operator.integrateddynamics.ingredients.with_item.info\": \"주어진 재료 위치에서 주어진 아이템으로 주어진 재료의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.ingredients.with_fluid\": \"유체로 재료\",\n    \"operator.integrateddynamics.ingredients.with_fluid.info\": \"주어진 재료 위치에서 주어진 유체로 주어진 재료의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.ingredients.with_energy\": \"에너지로 재료\",\n    \"operator.integrateddynamics.ingredients.with_energy.info\": \"주어진 재료 위치에서 주어진 에너지로 주어진 재료의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.ingredients.with_items\": \"아이템 리스트로 재료\",\n    \"operator.integrateddynamics.ingredients.with_items.info\": \"주어진 재료 위치에서 주어진 품목 리스트로 주어진 재료의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.ingredients.with_fluids\": \"유체 리스트로 재료\",\n    \"operator.integrateddynamics.ingredients.with_fluids.info\": \"주어진 재료 위치에서 주어진 유체 리스트로 주어진 재료의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.ingredients.with_energies\": \"에너지 요소로 재료\",\n    \"operator.integrateddynamics.ingredients.with_energies.info\": \"주어진 재료 위치에서 주어진 에너지 요소 리스트로 주어진 재료의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.recipe\": \"레시피\",\n    \"operator.integrateddynamics.recipe.basename\": \"레시피 %s\",\n    \"operator.integrateddynamics.recipe.input\": \"레시피 입력 재료\",\n    \"operator.integrateddynamics.recipe.input.info\": \"주어진 레시피의 입력 재료\",\n    \"operator.integrateddynamics.recipe.output\": \"레시피 출력 재료\",\n    \"operator.integrateddynamics.recipe.output.info\": \"주어진 레시피의 출력 재료\",\n    \"operator.integrateddynamics.recipe.with_input\": \"입력 재료로 레시피\",\n    \"operator.integrateddynamics.recipe.with_input.info\": \"주어진 재료를 입력으로 주어진 레시피의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.recipe.with_output\": \"출력 재료로 레시피\",\n    \"operator.integrateddynamics.recipe.with_input_output\": \"입력과 출력 재료로 레시피\",\n    \"operator.integrateddynamics.recipe.with_input_output.info\": \"첫번째 재료를 입력으로하고 두번째 재료를 출력으로하여 주어진 레시피의 사본을 가져옵니다.\",\n    \"operator.integrateddynamics.general\": \"기본\",\n    \"operator.integrateddynamics.general.basename\": \"기본 %s\",\n    \"operator.integrateddynamics.general.choice\": \"선택\",\n    \"operator.integrateddynamics.general.choice.info\": \"첫번째 값이 참이면 두번째 값을 가져오고 아니라면 세번째 값을 가져옵니다.\\\\n두번째와 세번째의 자료형은 같아야만합니다.\",\n    \"operator.integrateddynamics.general.identity\": \"항등식\",\n    \"operator.integrateddynamics.general.identity.info\": \"입력값의 복사본.\",\n    \"operator.integrateddynamics.general.constant\": \"상수식\",\n    \"operator.integrateddynamics.general.constant.info\": \"첫번째 입력값의 복사본.\",\n    \"operator.integrateddynamics.general.isnull\": \"null 맞음\",\n    \"operator.integrateddynamics.general.isnull.info\": \"주어진 값이 null 인가\",\n    \"operator.integrateddynamics.general.isnotnull\": \"null 아님\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"주어진 값이 null이 아닌가\",\n    \"operator.integrateddynamics.virtual\": \"가상 연산자\",\n    \"operator.integrateddynamics.virtual.basename\": \"가상 %s\",\n    \"operator.integrateddynamics.virtual.p_conjunction\": \"술어 논리곱\",\n    \"operator.integrateddynamics.virtual.p_conjunction.info\": \"두 술어의 논리곱\",\n    \"operator.integrateddynamics.virtual.p_disjunction\": \"술어 논리합\",\n    \"operator.integrateddynamics.virtual.p_disjunction.info\": \"두 술어의 논리합\",\n    \"operator.integrateddynamics.virtual.p_negation\": \"술어 부정\",\n    \"operator.integrateddynamics.virtual.p_negation.info\": \"술어의 부정\",\n    \"operator.integrateddynamics.virtual.piped\": \"파이프 연산자\",\n    \"operator.integrateddynamics.virtual.piped.info\": \"연결된 연산자\",\n    \"operator.integrateddynamics.virtual.piped2\": \"파이프 2 연산자\",\n    \"operator.integrateddynamics.virtual.piped2.info\": \"2개의 연결된 연산자\",\n    \"operator.integrateddynamics.cast\": \"캐스팅\",\n    \"operator.integrateddynamics.cast.basename\": \"캐스팅\",\n    \"operator.integrateddynamics.cast.tooltip\": \"%s을(를) %s로 캐스팅\",\n    \"operator.integrateddynamics.parse\": \"파싱\",\n    \"operator.integrateddynamics.parse.basename\": \"파싱\",\n    \"operator.integrateddynamics.parse.tooltip\": \"%s 파싱\",\n    \"info_book.integrateddynamics.section.main\": \"통합의 역학에 대하여\",\n    \"info_book.integrateddynamics.tag_index\": \"&o인덱스&r\",\n    \"info_book.integrateddynamics.introduction\": \"서론\",\n    \"info_book.integrateddynamics.introduction.text1\": \"오랜 시간 동안, 자동화 가능성은 너무 제한되어있었습니다. &1Integrated Dynamics&0 는 논리 시스템 자동화를 위한 필수 모드가 되는 것을 목표로 합니다. 이를 수행하기 위해 &l마인크래프트&r 및 그 모드의 대부분의 요소에 대한 인용 및 지원을 통해 표현형 몰입형 논리 프로그래밍 경험을 제공합니다.\",\n    \"info_book.integrateddynamics.introduction.text2\": \"레드스톤 회로는 항상 매우 기본적이었지만 실행 가능하고 쉽게 이해할 수 있는 자동화 방법이었습니다. 그러나 레드스톤의 기본성은 또한 불편한 점이기도 합니다. 복잡성이 더 높은 자동화 네트워크의 경우 레드스톤 회로가 매우 커질 수 있으며 결과적으로 관리하기 어려울 수 있습니다. 또한, 작은 누수 하나가 모든 레드스톤 사용자에게 심각한 두통을 유발할 수 있습니다.\",\n    \"info_book.integrateddynamics.introduction.text4\": \"하단에 있는 화살표를 클릭하여 이 책을 탐색할 수 있으며, Shift 키를 클릭하면 한 페이지가 아닌 전체 섹션으로 넘어갈 수 있습니다. 큰 섹션이 시작되면 더 빠른 탐색을 위해 하위 섹션에 대한 하이퍼링크가 포함된 콘텐츠 테이블이 표시됩니다. 레시피를 발견했을 때 이 책에 설명된 아이템을 클릭하면 해당 아이템이 설명된 섹션으로 빠르게 참조할 수 있습니다. 또한 이 책의 왼쪽 상단에 있는 버튼을 누르면 당신의 현재 것의 위의 섹션으로 이동할 수 있습니다. 오른쪽 상단 모서리에 있는 버튼은 클릭할 때마다 이전에 방문한 페이지로 돌아갑니다.\",\n    \"info_book.integrateddynamics.tutorials\": \"튜토리얼\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"서론\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"책의 이 부분에는 모드의 기본 사항을 이해하는 데 도움이 되는 여러 튜토리얼이 포함되어 있습니다. 또한 각 튜토리얼의 업적을 달성하면 일부 &l보상&r을 얻을 수 있습니다.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"멘릴의 기본\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"이 모드를 시작하려면 &l메네글린 생물군계&r에서 가장 쉽게 찾을 수 있는 &l멘릴 나무&r를 찾아야 합니다.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"이 과정을 반복하려면 레드스톤 펄스를 적용하여 &l압착기&r를 초기화할 수 있습니다.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"앞으로 더 쉽게 자동화할 수 있지만 비용이 더 많이 드는 &l기계식 압착기&r 및 &l기계식 건조대&r과 같은 업그레이드를 제작할 수도 있습니다.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"네트워크 기초\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"이 모드의 주요 목표는 자동화를 위한 제어 네트워크를 구축하는 것입니다. &l논리 케이블&r을 사용하여 수행할 수 있습니다. 그 케이블 10개로 네트워크를 만들어 보세요.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"다음 단계는 &l변수 카드&r 및 &l변수 변환기&r를 만드는 것입니다. &l변수 카드&r는 네트워크의 값에 대한 참조를 저장하는 데 사용합니다. &l변수 변환기&r는 이러한 값을 읽고 쓰기 위해 &l논리 케이블&r에 배치할 수 있는 구성 요소를 만드는 요소입니다.\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"기본 네트워크 구성 요소\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"&l변수 카드&r를 특정 값에 바인딩하는 방법 중 하나는 &l판독기&r에서 값을 읽는 것입니다. 몇 가지 기본 판독기를 만드는 것으로 시작하겠습니다. 그런 다음 &l변수 카드&r의 값을 표시하는 데 유용한 &l디스플레이 패널&r을 만드세요.\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"기능\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"간단한 예로 레드스톤 값을 읽어서 표시해 보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"그런 다음 &l레드스톤 판독기&r를 우클릭하여 GUI를 연 다음 &l변수 카드&r를 &6Integer&0 &l레드스톤 값&r 기능에 삽입하여 대상의 레드스톤 값을 해당 &l변수 카드&r에 바인딩해야 합니다.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"다음 단계로 &l레드스톤 작성기&r를 만들고 레드스톤 판독기가 읽은 최대 정수 레드스톤 값을 쓰도록 하세요.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"논리 연산\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"판독기의 값을 표시하는 것이 유용할 수 있지만 &2연산자&0를 사용하여 특정 값을 다른 값과 결합하기 시작하면 상황이 매우 흥미로워집니다. 모든 &2연산자&0는 &l논리 프로그래머&r에서 찾을 수 있습니다.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"항목과 블록 모습으로 존재하는 &l논리 프로그래머&r를 만드는 것으로 시작하세요. 모든 &l연산자&r의 목록을 보려면 GUI를 여세요.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"다음으로 &6Integer&0 연산자를 검색하고 클릭합니다. 그러면 숫자를 입력해야 하는 창이 열립니다. 이 작업을 완료했으면 빈 &l변수 카드&r를 삽입하여 해당 &6Integer&0를 &l변수 카드&r에 바인딩하십시오. 완료되면 다른 &6Integer&0에 대해 다시 한 번 수행하세요.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"마지막으로 &l덧셈&r 연산자를 검색하여 엽니다. 이렇게 하면 두 개의 &eNumber&0를 더할 수 있습니다. 두 개의 &6Integer&0 &l변수 카드&r를 이 슬롯에 삽입하고 새 &l변수 카드&r에 바인딩하세요.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"레시피 다루기\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text1\": \"&o레시피&r는 &l마인크래프트&r의 큰 부분입니다. 이것이 이 모드가 &8레시피&0 값과 입력 및 출력 컬렉션을 나타내는 &l재료&r 값을 통해 최상의 지원을 제공하는 이유입니다. 이 튜토리얼에서는 레시피를 얻고 사용하는 방법을 살펴보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text2\": \"&8레시피&0를 얻는 가장 쉬운 방법은 레시피를 처리할 수 있는 기계에서 읽는 것입니다. 먼저 &l기계 판독기&r를 &l제작대&r에 놓고 레시피 목록을 읽어 모든 제작 레시피를 읽어보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text3\": \"레시피를 좀 더 세밀하게 제어하고 싶거나 &l기계 판독기&r를 통해 레시피를 공개하지 않는 기계로 작업하는 경우 &l논리 프로그래머&r에서 직접 &8레시피&0를 시각적으로 만들 수 있습니다. 예를 들어 &l상자&r 제작과 같은 간단한 레시피를 만들어 보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text4\": \"이 튜토리얼의 마지막 부분에서 우리는 한 단계 더 나아가 레시피를 읽을 것입니다. &l출력에 대한 레시피&r 기능을 사용하여 &8재료&0 인스턴스에 적용하면 지정된 &8재료&0를 출력으로 하는 첫 번째 &8레시피&0를 반환하는 연산자를 검색할 수 있습니다. 우리는 이것을 사용하여 &l상자&r의 레시피를 찾을 것입니다.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text5\": \"&l논리 프로그래머&r에서 단일 &8상자&0가 있는 &8재료&0 인스턴스를 만드는 것으로 시작하겠습니다. 다음으로 &l제작대&r를 대상으로 하는 &l기계 판독기&r에서 &l출력에 대한 레시피&r 측면을 읽습니다. 그런 다음 &o적용&r 연산자를 사용하여 &l기계 판독기&r에서 가져온 연산자에 &l상자&r &8재료&0를 적용합니다. 마지막으로 결과 변수를 &l디스플레이 패널&r에 삽입하여 &l상자&r의 레시피를 확인하세요. (사용한 모든 변수를 &l변수 저장소&r에 삽입하는 것을 잊지 마세요).\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text6\": \"분명히 &8레시피&0 및 &8재료&0로 할 수 있는 일은 훨씬 더 많습니다. 사용 가능한 다른 고급 기능을 알아보려면 &l기계 판독기&r 및 &l논리 프로그래머&r를 살펴보십시오.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text1\": \"&4NBT&0 태그는 복잡한 데이터 구조를 나타내는 데 사용됩니다. 이것들은 일반적으로 마인크래프트에서 내부적으로 기계 및 개체 정보를 저장하는 데 사용됩니다. 이 튜토리얼에서는 간단한 NBT 태그 읽기 및 조작에 대해 알아보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text2\": \"첫 번째 단계로 독서대에 &l블록 판독기&r를 사용하고 &4NBT&0 데이터를 읽어 &l디스플레이 패널&r에서 시각화합니다.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text3\": \"다음으로 읽은 &l독서대&r &4NBT&0 태그에서 &3책&0 하위 태그를 복합 태그로 읽고 시각화합니다.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text5\": \"최종 발전과제를 달성하려면 &3책&0/&3ID&0 필드를 선택하는 &lNBT 경로&r 표현식을 만들고 이를 &l독서대&r 태그에 적용하고 &l디스플레이 패널&r에서 시각화합니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"고급 네트워크 구성 요소\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"이제 기본적인 논리에 익숙해졌으므로 더 고급 네트워크 구성요소인 &l구체화장치&r 및 &l프록시&r를 살펴보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"기억하시겠지만 &l논리 프로그래머&r를 사용하면 &o정적인&r 값으로 변수를 만들거나 &o동적인&r 값 평가를 기반으로 더 복잡한 변수를 만들 수 있습니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"&l구체화장치&r는 &o동적인&r 값을 &o정적인&r 값으로 변환하는 데 사용할 수 있습니다. 이것은 &l구체화장치&r의 상단 슬롯에 &o동적인&r &l변수 카드&r를 삽입하여 수행할 수 있습니다. 왼쪽 슬롯에 삽입된 모든 &l변수 카드&r는 해당 시점의 상단 변수 값으로 설정됩니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"&l프록시&r를 사용하면 변수에 대한 간접적인 참조 레벨을 추가할 수 있습니다. 예를 들어 &o정적인&r 값이 필요한 논리 프로그램 있다고 합시다. 미래의 어느 시점에서든 해당 값을 변경하려면 해당 값이 &o프록시&r되어있지 않는 한 프로그램을 다시 작성해야 합니다. 이 경우 &l프록시&r의 값을 업데이트하기만 하면 됩니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"이 튜토리얼에서는 구체화되고 프록시된 &l변수 카드&r를 만듭니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"고급 연산\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"지난 튜토리얼에서 눈치채셨겠지만 &l논리 프로그래머&r를 사용하면 단순히 숫자를 추가하는 것 이상의 작업을 수행할 수 있습니다. 이 튜토리얼에서 우리는 더 고급인 데이터 구조와 연산자의 연결을 살펴볼 것입니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"&6Integer&0인 &61&0, &610&0 과 &6100&0이 들어있는 정적 &7리스트&0를 생성하여 시작하고 &l디스플레이 패널&r에서 시각화해 보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7아이템&0은 이 모드에서 가능한 자료형 중 하나입니다. 정적 &7아이템&0 &l변수 카드&r를 만들고 해당 아이템에서 모드 이름을 동적으로 검색하고 &l디스플레이 패널&r에서 시각화해 보겠습니다.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"참조된 변수를 저장하려면 &l변수 저장소&r를 사용해야 한다는 것을 기억하세요.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"연산자 값\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"첫 번째 단계로 &l논리 프로그래머&r로 이동하여 자동 완성 드롭다운 목록에 &o산수 덧셈&r &2연산자&0를 입력하고 클릭하여 &l변수 카드&r에 작성하여 새로운 정적 &2연산자&0를 만듭니다. 그런 다음 두 개의 &6Integer&0 변수를 만듭니다. 다음으로 &o적용&r 연산자를 찾고 첫 번째 &6Integer&0를 &2연산자&0에 적용하면 추가 &2연산자&0의 첫 번째 입력 인수가 &6Integer&0에 &o바인딩&r되고 하나의 나머지 입력 인수와 함께 새 &2연산자&0가 출력됩니다. 다음으로 &o적용&r 연산자를 다시 사용하여 새로 생성된 &2연산자&0의 최종 입력 인수를 &o바인딩&r합니다. 마지막으로 &l디스플레이 패널&r을 사용하여 동적으로 호출된 덧셈을 시각화합니다.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"&2연산자&0를 적용하는 것 외에 다른 많은 고급 &2연산자&0 작업이 있습니다. 이제 &7리스트&0, 보다 구체적으로 &7리스트&0 및 &o술어&r(하나의 입력 인수와 &9Boolean&0 출력 값이 있는 &2연산자&0)을 사용하는 &o필터&r &2연산자&0에서 &2연산자&0를 사용하는 방법을 살펴보겠습니다. &6Integer&0인 &61&0, &610&0 과 &6100&0의 &7리스트&0를 작성하여 시작하세요. 다음으로 &o관계 같음&r &2연산자&0를 변수에 저장합니다. 다음으로 이 연산자를 &6Integer&0 &610&0에 적용합니다. 다음으로 &7리스트&0 및 적용된 &2연산자&0와 함께 &o필터&r &2연산자&0를 사용합니다. 이것은 주어진 연산자에 적용하면 &9거짓&0을 반환하는 &7리스트&0의 모든 항목을 걸러냅니다. 이 경우 &610&0과 같지 않은 모든 요소가 목록에서 제거됨을 의미합니다. &l디스플레이 패널&r에서 결과를 시각화합니다.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"&o술어&r 결합, &2연산자&0 인수 뒤집기 및 &7리스트&0에 대한 추가 작업과 같이 &2연산자&0에 대한 많은 다른 작업이 있습니다.\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"도전\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"이 마지막 튜토리얼 섹션에는 &1Integrated Dynamics&0의 기본 사항을 이해하고 있음을 보여주기 위한 몇 가지 작은 문제가 포함되어 있습니다. 각 도전에 대한 추가 정보는 제공되지 않으므로 행운을 빕니다!\",\n    \"info_book.integrateddynamics.manual\": \"매뉴얼\",\n    \"info_book.integrateddynamics.manual.introduction\": \"소개\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"필요한 것\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"멘릴 나무\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"때때로 키가 큰 푸른 나무를 만날 수 있습니다. 이러한 나무는 &l멘릴 나무&r라고 하며 몇 가지 매우 흥미로운 속성을 가지고 있습니다. 먼저 잎에서 빛이 납니다. 이것은 매우 맛있는 빛나는 열매로 인해 발생합니다! 둘째, 멋지게 보이는 나무에는 이 모드에서 대부분의 것을 만드는 데 사용할 수 있는 많은 수지가 포함되어 있습니다.\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"메네글린 생물군계\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"&l멘릴 나무&r는 불행히도 찾기가 매우 어렵습니다. 운 좋게도 이 나무가 번성하는 &l메네글린 생물군계&r라는 생물군계가 있습니다. 많은 양의 &l멘릴 나무&r는 이 생물 군계의 일부를 파란색으로 만듭니다. 이는 이 나무가 환경에 방출하는 &l멘릴 수지&r 때문입니다.\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"압착기\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"건조대\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"&l건조대&r를 사용하여 블록과 아이템을 건조시킬 수 있습니다. 이것은 예를 들어 &l멘릴 수지&r를 &l결정화된 멘릴&r로 건조시키려는 경우에 유용할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"뭘 하든, 뜨거운 유체를 넣지 마세요!\",\n    \"info_book.integrateddynamics.manual.networks\": \"네트워크\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"&lIntegerated Dynamics&r의 &l네트워크&r는 모든 우수한 자동화 시스템의 기초입니다. 네트워크는 연결된 &l논리 케이블&r의 모음으로 구성되며 여기에 부품을 배치할 수 있습니다. 이러한 케이블은 &l렌치&r을 사용하여 관리해야 합니다. 렌치를 들고 케이블이나 부품을 Shift 키를 누른 상태에서 우클릭하면 제거되고 그냥 우클릭하면 다른 케이블과의 연결이 끊어집니다.\",\n    \"info_book.integrateddynamics.manual.logic\": \"논리\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"자료형\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"가장 단순한 자료형인 해당 값은 &9참&0 또는 &9거짓&0이 될 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"양수 및 음수인 정수를 나타내는 자료형입니다. 예: &6-20&0, &60&0, &61000&0, &6333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"값의 범위는 &6–2147483648&0에서 &62147483647&0입니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"이 자료형은 &lNumber&r 카테고리의 일부입니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"양수 또는 음수일 수도 있는 실수입니다. 예: &e3.1415&0, &e4815162342&0, &e3.33333333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"이 자료형은 &lNumber&r 카테고리의 일부입니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"&6Integer&0와 동일하지만 &e-9223372036854775808&0에서 &e9223372036854775807&0까지로 값 범위가 더 큽니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"이 자료형은 &lNumber&r 카테고리의 일부입니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"일련의 문자. 예: &4”aaa”&0, &4“Hi!”&0, &4“x!(àà)ç”&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator\": \"연산자\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"&2연산자&0에 대한 참조를 포함하는 값. 연산자는 &l커링&r의 개념을 기반으로 보다 유연한 평가를 위해 부분적으로 적용될 수 있습니다. 이것은 연산자가 다른 연산자에 의해 데이터로 취급되는 &l메타프로그래밍&r 개념의 일부입니다. 이를 통해 기본적으로 새로운 연산자를 직접 생성할 수 있으며 더 복잡한 작업에 사용할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"예를 들어 덧셈 &2연산자&0를 변수 카드에 저장할 수 있습니다. 연산자 이름 입력을 시작하고 자동 완성 드롭다운 목록에서 선택하고 클릭하면 됩니다. 그런 다음 하나의 &6Integer&0 &l변수 카드&r로 이를 부분적으로 적용할 수 있습니다. 그러면 하나의 입력 유형과 하나의 출력 유형이 있는 새 가상 연산자가 생성되며, 여기에는 적용된 &6Integer&0에 대한 참조가 내부적으로 포함됩니다. 다른 &6Integer&0를 적용하면 &6Integer&0가 됩니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"목록을 단일 값으로 축소하거나 &l술어&r를 기반으로하는 리스트 필터링과 같은 고급 평가를 위한 다른 작업의 &2연산자&0가 &l논리 프로그래머&r에 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"&l마인크래프트&r의 특정 아이템은 내부적으로 &3NBT&0 데이터 구조를 사용합니다. &3NBT&0 태그는 다른 자료형의 여러 값을 보유할 수 있는 특수한 종류의 개체입니다. 예를 들어 모든 아이템을 포함하는 &8상자&0의 NBT 표현, &8곡괭이&0의 마법 부여 정보, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"변수는 이러한 &3NBT&0 태그에 대한 참조를 보유할 수 있으며 해당 연산자는 이러한 태그를 읽거나 확장하기 위해 존재합니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"&3NBT&0 태그는 일반적으로 전용 연산자를 사용하여 특정 정보를 읽을 수 없는 경우에 대비하여 이 모드에서 낮은 수준의 대체 메커니즘으로 사용됩니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block\": \"블록\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"&l마인크래프트&r 블록에 대한 참조. 예를 들면 &8흙&0, &8조약돌&0, &8빨간색 테라코타&0, &8상자&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"아이템\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"&l마인크래프트&r 아이템에 대한 참조. 예를 들면 &8사과&0, &8조약돌&0, &8다이아몬드 곡괭이&0, &8마법 부여된 검&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"개체\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"특정한 &l마인크래프트&r 개체에 대한 참조. 예를 들면 &8좀비&0, &8소&0, &8플레이어_abc&0 ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"유체\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"&l마인크래프트&r 유체에 대한 참조. 예를 들면 &8용암@1000mB&0, &8물@3mB&0, &8멘릴 수지@3030mB&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"재료\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients.text1\": \"&l아이템&r, &l유체&r 및 &l에너지 양&r(&6Integer&0)의 컬렉션. 이것의 주된 쓰임새는 &8레시피&0의 입력과 출력을 대표하는 것입니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"레시피\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text1\": \"&8재료&0를 입력으로 사용하고 다른 &8재료&0 값을 출력하는 추상적인 제작 레시피.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text2\": \"레시피는 &l논리 프로그래머&r를 사용하여 시각적으로 만들거나 &l기계 판독기&r를 사용하여 (지원되는) 기계에서 읽을 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text3\": \"이 조리법은 &lIntegerated Crafting&r과 같은 자동 제작 모드에 사용할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text4\": \"GUI 내 기존 &l변수 카드&r 중에서 특정 레시피를 빠르게 찾으려면 Shift 키를 누른 상태에서 레시피의 출력 아이템이 무엇인지 일시적으로 표시할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"변수\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"변수 카드\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&l변수 카드&r는 이 모드에서 논리 전송의 기본 양식입니다. 변수에는 특정 &l자료형&r이 있는 특정 &l값&r에 대한 참조가 포함되어 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"각 &l변수 카드&r에는 고유 ID가 있으며, 이는 &l논리 평가&r용 네트워크에서 내부적으로 사용됩니다. 네트워크에서 오류가 발생하면 이러한 ID가 플레이어에게 표시되어 디버깅에 도움이 됩니다. 더 나은 사용성을 위해 플레이어가 ID 대신 라벨을 볼 수 있도록 &l라벨러&r를 사용하여 &l변수 카드&r에 라벨을 지정할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"변수 저장소\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"일부 동적 변수는 참조된 변수에 접근할 수 있어야 하는 다른 변수를 기반으로 평가됩니다. 두 개의 다른 변수의 덧셈을 나타내는 동적 변수는 평가될 때 해당 두 개의 다른 변수에 접근할 수 있어야 합니다. 다른 변수에 대한 접근은 동적 변수가 평가되는 것과 동일한 네트워크 내에서 가능해야 하며, 이는 다른 변수 카드를 &l변수 저장소&r에 배치하여 수행할 수 있습니다. 네트워크에 여러 &l변수 저장소&r이 있을 수 있습니다. 이러한 다른 변수는 동일한 네트워크에도 있어야 하는 특정 변수를 참조할 수도 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.advanced\": \"고급\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path\": \"NBT 경로\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text4\": \"필드 이름 배열 또는 와일드카드를 제공하여 여러 필드를 선택할 수 있습니다(예: &4[fieldA,fieldB]&0 또는 &4*&0).\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text6\": \"NBT 목록으로 작업할 때 &4[1]&0과 같은 인덱스를 지정하여 특정 항목을 선택할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text7\": \"예를 들어 &4”.a[1]”&0 표현식을 &3NBT&0 태그 &3{ a: [10,20] }&0에 적용하면 &3”20”&0이 출력됩니다.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text8\": \"슬라이스 연산자 &4[start:end:step]&0을 사용하여 여러 목록 인덱스를 선택할 수 있습니다. (시작만 필요하고 다른 요소는 생략 가능)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text9\": \"예를 들어 &4”.a[1:4:2]”&0 표현식을 &3NBT&0 태그 &3{ a: [0,1,2,3,4,5] }&0에 적용하면 &3[1,3]이 출력됩니다. &0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text10\": \"&4[?(@.childName < 10)]&0(@은 현재 태그, ..는 상위 태그, $는 루트 태그를 나타냄)와 같은 필터 구문을 사용하여 고급 필터 표현식을 사용할 수도 있습니다.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text11\": \"예를 들어 &4”$.a[?(@ == 3)]”&0 표현식을 &3NBT&0 태그 &3{ a: [0,1,2,3,4,5] }&0에 적용하면 &3[3 ]&0.\",\n    \"info_book.integrateddynamics.manual.parts\": \"부품\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"소개\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text3\": \"부품의 &l에너지 채널&r은 부품이 에너지를 끌어올 수 있는 위치를 결정합니다. -1은 와일드카드 채널(모든 채널과 일치함)이고 0은 기본 채널(채널 설정이 없는 에너지 셀과 같은 부분에서 사용됨)입니다. 마지막으로 &l대상 면&r은 대상 블록의 어느 면과 상호 작용할지 결정합니다. 이것은 실제로 배치되는 면으로 기본 설정됩니다.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text4\": \"일부 판독기 및 작성기 부품에는 특정 다른 모드가 있는 경우에만 기능이 포함됩니다. 특정 부품에 대한 기능이 없으면 제작을 시도할 때마다 아이템 툴팁에 경고가 추가됩니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"작성기\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"소개\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"소리\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"환경에서 재생되는 음악 및 소리에 대한 정보입니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"블록\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"부품이 향하고 있는 블록에 대한 정보입니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"개체\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"부품이 향하고 있는 사각형 내부에 있는 개체 또는 개체에 대한 정보입니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"추가 차원\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"온라인 플레이어 목록과 같은 서버에 대한 정보입니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"유체\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"이 부품이 향하고 있는 유체 탱크에서 값을 읽습니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"인벤토리\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"향하고 있는 블록에서 슬롯 및 아이템의 양과 같은 정보를 읽습니다. 이것은 아이템 자체를 전송하지는 않습니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"기계\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"이 부품이 향하고 있는 기계에서 하고 있는 작업과 같은 것들을 읽을 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"네트워크\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"이 부품이 향하고 있는 &lIntegrated Dynamics&r 네트워크에 대한 정보를 읽습니다. 이 부품이 있는 네트워크와 동일한 네트워크일 필요는 없습니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"레드스톤\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"향하고 있는 블록에서 레드스톤 정보를 읽습니다.\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"세계\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"현재 날씨 및 시간과 같은 이 부품이 속한 세계에 대한 정보입니다.\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"작성기\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"소리\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"소리와 음악을 재생합니다.\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"효과\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"특정 값과 성정에 기반한 입자를 방출합니다.\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"개체\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"개체를 구성하고 제어합니다.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"기계\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"기계를 구성하고 제어합니다.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"인벤토리\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"인벤토리 및 파이프와 관련된 작업을 수행합니다. 이것은 아이템 자체를 전송하지 않습니다.\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"레드스톤\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"특정 레드스톤 세기를 출력합니다.\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"나머지\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"밝기\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"항상 켜져 있거나 &1Boolean&0 또는 &6Integer&0 &l변수 카드&r를 사용하여 제어할 수 있는 간단한 조명.\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"디스플레이\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"&l변수 카드&r의 값을 단순히 보여주는 유용한 모니터링 또는 디버깅 도구. &l디스플레이&r는 &l렌치&r로 우클릭하여 회전할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text2\": \"GUI 내부에서 &l복사&r 버튼을 사용하여 현재 표시된 값을 게임 외부에서 사용할 수 있도록 클립보드에 복사할 수 있습니다. OS의 기본 복사 단축키를 사용하여 이 값을 복사할 수도 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"단방향 커넥터\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"때로는 논리 네트워크가 먼 거리에 걸쳐 있습니다. 일반 케이블을 사용하면 문제가 해결되지만 장거리에 케이블을 배치하는 것은 그리 편리하지 않을 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&l단방향 커넥터&r을 사용하면 장거리에서 네트워크를 무선으로 연결할 수 있습니다. 이것은 한 네트워크에 첫 번째 커넥터를 배치하고 다른 네트워크에 두 번째 커넥터를 배치하여 수행할 수 있습니다. 이 두 커넥터는 512블록 이하의 거리로 서로에 대해 직선이어야 합니다. 커넥터는 유효한 연결이 설정되면 활성 상태로 나타납니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"전방향 커넥터\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"&l단방향&r 커넥터는 한 방향의 장거리 연결에 유용하지만 어떤 경우에는 너무 제한적일 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"예를 들어 단일 커넥터를 사용하여 두 개 이상의 네트워크를 함께 연결하거나 다른 차원의 네트워크와 연결할 수는 없습니다. 이러한 경우 &l전방향&r 커넥터를 사용할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"이러한 커넥터는 처음 제작될 때 네트워크를 연결할 수 있는 그룹을 식별하는 고유한 그룹 ID를 갖습니다. 각 커넥터 그룹은 월드에 배치될 때 고유한 입자 색상을 가지며 그룹 ID는 아이템 툴팁에서 또는 배치된 후 커넥터를 우클릭하여 볼 수 있습니다. 그룹 내의 기존 커넥터를 사용하여 새 커넥터를 만들어 그룹에 커넥터를 추가할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"두 가지 방법으로 기존 커넥터 그룹을 변경할 수도 있습니다. 제작대에서 하나만 제작하면 다른 구성원이 없는 새 그룹에 할당되고 두 개를 함께 제작하면 첫 번째 그룹의 그룹이 두 번째 그룹에 복사됩니다.\",\n    \"info_book.integrateddynamics.manual.machines\": \"기계\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"구체화장치\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"동적 &l변수 카드&r를 정적으로 만들기 위해 &l구체화장치&r를 사용할 수 있습니다. 동적 &l변수 카드&r를 삽입해야 하고 새로운 빈 &l변수 카드&r를 삽입해야 동적 &l변수 카드&r의 현재 값을 정적 값으로 포함하게 됩니다.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"이것은 특정 동적 &l변수 카드&r의 값을 기록하려는 상황이나 &l변수 카드&r의 동적 평가가 더 이상 필요하지 않은 상황에서 유용할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&l변수 카드&r는 &l구체화장치&r에서 파이프로 연결 및 제거할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"프록시\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"&l프록시&r는 특정 &l변수 카드&r에 대한 동적 참조를 만드는 데 사용할 수 있습니다. 이를 통해 상단 슬롯에 &l변수 카드&r를 삽입하고 프록시에 있는 &l변수 카드&r 값을 동적으로 사용하는 새 &l변수 카드&r를 작성할 수 있습니다. 참조된(또는 다른) &l변수 카드&r는 프록시가 작동하려면 맨 위 슬롯에 남아 있어야 합니다.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"이것은 &l변수 카드&r 체인이 있지만 여전히 하나의 값을 수동으로 변경할 수 있기를 원하는 상황에서 유용합니다. 그런 다음 해당 변수를 프록시하여 언제든지 다른 변수와 교환할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"&l구체화장치&r와 마찬가지로 &l변수 카드&r는 파이프로 연결되거나 빠져나갈 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"어떤 상황에서는 일종의 변수 값 기록을 유지해야 할 수도 있습니다. 예를 들어 이전 값에 대한 평균 또는 차이를 구하려는 경우.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"에너지 배터리\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"&lIntegrated Dynamics&r 네트워크는 에너지를 소비하도록 구성할 수 있습니다. 이를 위해 &l에너지 배터리&r를 네트워크에 연결하여 에너지 저장 용량을 추가할 수 있습니다.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"레드스톤 신호를 적용하면 모든 면으로 에너지 자동 내보내기 모드가 활성화됩니다.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"이 배터리가 인벤토리에 있을 때 Shift+우클릭으로 &la자동 공급&r 모드를 활성화할 수 있습니다. 활성화되면 이 배터리는 자동으로 두 손에 들고 있는 아이템을 에너지로 채우려고 시도합니다.\",\n    \"info_book.integrateddynamics.manual.machines.generator\": \"발전기\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"기계식 압착기\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"기계식 건조대\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"&l건조대&r로 건조하는 데 시간이 걸릴 수 있습니다. 더 빨리 진행하고 싶거나 더 많은 것을 말리고 싶다면 &l기계식 건조대&r를 사용할 수 있습니다. 이는 작동하는 데 에너지가 필요합니다.\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"논리 프로그래밍\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"동적 변수 카드를 만드는 또 다른 방법은 하나 이상의 &l변수 카드&r에 &l연산자&r를 적용하여 새로운 &l변수 카드&r를 생성하는 것입니다. &l연산자&r의 예는 두 개의 &lNumber&r를 더하여 새로운 &lNumber&r을 생성하는 것입니다.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"&l논리 프로그래머&r에는 &l연산자&r의 대규모 컬렉션이 포함되어 있습니다. 또한 모든 &l자료형&r에 대해 정적 &l변수 카드&r를 생성할 수 있습니다. &l연산자&r를 선택한 후 유효한 &l변수 카드&r를 예상 입력 슬롯에 삽입해야 합니다. 마지막으로 다른 &l변수 카드&r를 출력 슬롯에 삽입해야 합니다. &l연산자&r가 유효한 경우 새 동적 변수가 카드에 기록됩니다. 참조된 &l변수 카드&r는 &l연산자&r가 평가될 동일한 네트워크의 &l변수 저장소&r에 있어야 합니다.\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/nl_nl.json",
    "content": "{\n    \"_comment\": \"------ Over de Dynamiek van Integratie inhoud ------\",\n    \"itemGroup.integrateddynamics\": \"Integrated Dynamics\",\n    \"general.integrateddynamics.description\": \"Neem volledige en geautomatiseerde controle over je apparaten.\",\n    \"general.integrateddynamics.info.none\": \"Geen\",\n    \"general.integrateddynamics.energy\": \"Energie\",\n    \"general.integrateddynamics.energy_unit\": \"RF\",\n    \"general.integrateddynamics.error.no_network\": \"Geen geldig netwerk gevonden\",\n    \"general.integrateddynamics.true\": \"Waar\",\n    \"general.integrateddynamics.false\": \"Onwaar\",\n    \"gui.integrateddynamics.part_settings\": \"Aanzetstukinstellingen\",\n    \"gui.integrateddynamics.aspect_settings\": \"Aspectinstellingen\",\n    \"gui.integrateddynamics.keybinding\": \"Toegewezen toets\",\n    \"gui.integrateddynamics.button.save\": \"Opslaan\",\n    \"gui.integrateddynamics.button.rename\": \"Naam wijzigen\",\n    \"gui.integrateddynamics.button.copy\": \"Kopiëren\",\n    \"gui.integrateddynamics.button.copy.info\": \"Kopiëren naar klembord\",\n    \"gui.integrateddynamics.button.edit\": \"Bewerken\",\n    \"gui.integrateddynamics.button.add\": \"Toevoegen\",\n    \"gui.integrateddynamics.button.remove\": \"Verwijderen\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"Ticks/bewerking\",\n    \"gui.integrateddynamics.partsettings.priority\": \"Prioriteit\",\n    \"gui.integrateddynamics.partsettings.channel\": \"Energiekanaal\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"Netwerk energieverbruik is uitgeschakeld op deze server.\",\n    \"gui.integrateddynamics.partsettings.side\": \"Doelwitzijde\",\n    \"gui.integrateddynamics.partoffset.slot.message.noint\": \"Ongeldig geheel getalcoördinaat\",\n    \"gui.integrateddynamics.delay.capacity\": \"Capaciteit\",\n    \"gui.integrateddynamics.part.properties\": \"Eigenschappen:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"Filter\",\n    \"gui.integrateddynamics.logicprogrammer.reset\": \"Wissen\",\n    \"gui.integrateddynamics.logicprogrammer.info.create\": \"§lKoppel nieuwe§r variabelekaart\",\n    \"gui.integrateddynamics.logicprogrammer.info.modify\": \"§lWijzig§r variabelekaart\",\n    \"gui.integrateddynamics.logicprogrammer.tooltip.writeslot.create\": \"Voeg een variabelekaart toe om te koppelen aan een §lnieuwe§r waarde\",\n    \"gui.integrateddynamics.logicprogrammer.tooltip.writeslot.modify\": \"Voeg een variabelekaart toe om zijn waarde te §lladen§r\",\n    \"gui.integrateddynamics.rewards\": \"Beloningen\",\n    \"gui.integrateddynamics.rewards.collect\": \"Verzamelen\",\n    \"gui.integrateddynamics.rewards.collected\": \"Verzameld\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"Kan beloningen niet verzamelen, is je inventaris vol?\",\n    \"gui.integrateddynamics.input\": \"Invoer: %s\",\n    \"gui.integrateddynamics.output\": \"Uitvoer: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"Automatisch vloeistof uitwerpen\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Automatisch vloeistof uitwerpen: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"Duw naar aangrenzende tanken.\",\n    \"gui.integrateddynamics.input_short\": \"Invoer\",\n    \"gui.integrateddynamics.output_short\": \"Uitvoer\",\n    \"gui.integrateddynamics.recipe.reusable\": \"Herbruikbaar\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Integrated Dynamics - Netwerkdiagnostiek\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"Aanzetstukken\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Netwerk\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Kabels\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"Aanzetstuk\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Ticktijd (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"Dimensie\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"Positie\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"Zijde\",\n    \"gui.integrateddynamics.diagnostics.table.actions\": \"Acties\",\n    \"key.categories.integrateddynamics\": \"Integrated Dynamics\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"Focus zoekveld in logicaprogrammeur\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"Hernoem variabele in logicaprogrammeur\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"Pagina omslaan\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"Pagina's omslaan\",\n    \"block.integrateddynamics.cable\": \"Logicakabel\",\n    \"block.integrateddynamics.energy_battery\": \"Energiebatterij\",\n    \"block.integrateddynamics.energy_battery.info\": \"Plaats in het vervaardigingsrooster met andere energiebatterijen om de capaciteit te vergroten. Shift + rechtsklik om automatisch te bevoorraden.\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"Automatisch bevoorraden: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"Creatieve energiebatterij\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"Automatisch bevoorraden: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"Logicaprogrammeur\",\n    \"block.integrateddynamics.coal_generator\": \"Generator\",\n    \"block.integrateddynamics.coal_generator.info\": \"Produceert energie van brandstofvoorwerpen\",\n    \"block.integrateddynamics.proxy\": \"Proxy\",\n    \"block.integrateddynamics.proxy.info\": \"Verstrek een variabeleverwijzing als een aspect. Wis in een vervaardigingsrooster.\",\n    \"block.integrateddynamics.materializer\": \"Materialisator\",\n    \"block.integrateddynamics.materializer.info\": \"Materialiseer de waarde van een variabele in een nieuwe variabele\",\n    \"block.integrateddynamics.menril_log\": \"Menrilstam\",\n    \"block.integrateddynamics.menril_log_filled\": \"Verrijkte menrilstam\",\n    \"block.integrateddynamics.menril_log_stripped\": \"Gestripte menrilstam\",\n    \"block.integrateddynamics.menril_leaves\": \"Menrilbladeren\",\n    \"block.integrateddynamics.menril_sapling\": \"Menrilzaailing\",\n    \"block.integrateddynamics.menril_planks\": \"Menrilhouten planken\",\n    \"block.integrateddynamics.menril_slab\": \"Menrilhouten plaat\",\n    \"block.integrateddynamics.menril_fence\": \"Menrilhouten hek\",\n    \"block.integrateddynamics.menril_fence_gate\": \"Menrilhouten poort\",\n    \"block.integrateddynamics.menril_wood\": \"Menrilhout\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"Gestript menrilhout\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Gekristalliseerd menrilblok\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"Gekristalliseerde menrilbakstenen\",\n    \"block.integrateddynamics.block_menril_resin\": \"Menrilhars\",\n    \"block.integrateddynamics.drying_basin\": \"Droger\",\n    \"block.integrateddynamics.drying_basin.info\": \"Voor het drogen van voorwerpen en vloeistoffen\",\n    \"block.integrateddynamics.squeezer\": \"Samenperser\",\n    \"block.integrateddynamics.squeezer.info\": \"Perst samen, maakt plat en verplettert door er op te springen. Redstone-puls om terug te zetten.\",\n    \"block.integrateddynamics.menril_torch\": \"Menrilfakkel\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Menril stenen fakkel\",\n    \"block.integrateddynamics.invisible_light\": \"Onzichtbare lichtbron\",\n    \"block.integrateddynamics.menril_door\": \"Menril deur\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Menrilhouten trap\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Gekristalliseerde menril trap\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"Gekristalliseerde menrilbakstenen trap\",\n    \"block.integrateddynamics.delay\": \"Vertrager\",\n    \"block.integrateddynamics.delay.info\": \"Verstrek de geschiedenis van een variabele als een aspect. Wis in een vervaardigingsrooster.\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"Vloeibaar chorus\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"Gekristalliseerd chorusblok\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"Gekristalliseerde chorusbakstenen\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"Gekristalliseerde chorus trap\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"Gekristalliseerde chorusbakstenen trap\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"Mechanische samenperser\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"Perst samen, maakt plat en verplettert op energie.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"Mechanische droger\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"Voor het drogen van voorwerpen en vloeistoffen op energie.\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"Gekristalliseerde menril plaat\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"Gekristalliseerde menrilbakstenen plaat\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"Gekristalliseerde chorus plaat\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"Gekristalliseerde chorusbakstenen plaat\",\n    \"item.integrateddynamics.general.id\": \"§e§lID: §r%s\",\n    \"item.integrateddynamics.wrench\": \"Moersleutel\",\n    \"item.integrateddynamics.wrench.info\": \"Shift + rechtsklik in de lucht om van modus te wisselen\",\n    \"item.integrateddynamics.wrench.mode\": \"§eModus: §r%s\",\n    \"item.integrateddynamics.wrench.mode.default\": \"Standaard\",\n    \"item.integrateddynamics.wrench.mode.default.info\": \"Verwijder aanzetstukken of draai blokken\",\n    \"item.integrateddynamics.wrench.mode.offset.pos\": \"Positie: %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side.side\": \"Zijde: %s\",\n    \"item.integrateddynamics.variable\": \"Variabelekaart\",\n    \"item.integrateddynamics.variable.info\": \"Wis of kopieer in een vervaardigingsrooster\",\n    \"item.integrateddynamics.variable.warning\": \"§4§lWAARSCHUWING: NIET dit voorwerp kopiëren door te klikken met de middelste muisknop!\",\n    \"item.integrateddynamics.variable.id\": \"§e§oVariabele-ID: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"Labeler\",\n    \"item.integrateddynamics.labeller.info\": \"Labelt je variabelen. Werkt ook in de logicaprogrammeur.\",\n    \"item.integrateddynamics.labeller.button.write\": \"Opslaan\",\n    \"item.integrateddynamics.facade.info\": \"Combineer met een blok in een werkbank\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"Emmer vloeibaar chorus\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"Emmer menrilhars\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"Gekristalliseerd menrilfragment\",\n    \"item.integrateddynamics.variable_transformer\": \"Invoer/uitvoer variabelentransformators\",\n    \"item.integrateddynamics.variable_transformer_output\": \"Uitvoer variabelentransformator\",\n    \"item.integrateddynamics.variable_transformer_input\": \"Invoer variabelentransformator\",\n    \"item.integrateddynamics.menril_berries\": \"Menrilbessen\",\n    \"item.integrateddynamics.menril_berries.info\": \"Smaakvol en verlichtend\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"Draagbare logicaprogrammeur\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"Over de Dynamiek van Integratie\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"Een complete naslaghandleiding met lessen voor de mod\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"Gekristalliseerd chorusfragment\",\n    \"item.integrateddynamics.logic_director\": \"Logica-actualisator\",\n    \"item.integrateddynamics.proto_chorus\": \"Protochorus\",\n    \"fluid_type.integrateddynamics.menril_resin\": \"Menrilhars\",\n    \"fluid_type.integrateddynamics.liquid_chorus\": \"Vloeibaar chorus\",\n    \"advancement.integrateddynamics.squeezing\": \"Samenpersen als de beste\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"Maak een samenperser.\",\n    \"advancement.integrateddynamics.drying\": \"Drogen als de beste\",\n    \"advancement.integrateddynamics.drying.desc\": \"Maak een droger.\",\n    \"advancement.integrateddynamics.menril_production\": \"Menril productie\",\n    \"advancement.integrateddynamics.menril_production.desc\": \"Verwerk een menrilstam tot menrilhars en droog het om gekristalliseerd menril te krijgen.\",\n    \"advancement.integrateddynamics.cables_logic\": \"Kabels, logisch\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"Maak een logicakabel\",\n    \"advancement.integrateddynamics.networks_logic\": \"Netwerken, logisch\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"Bouw een logicanetwerk met ten minste 10 verbonden logicakabels.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"Menrilsleutelen\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"Maak een moersleutel\",\n    \"advancement.integrateddynamics.variables\": \"Variabelen, waardevol\",\n    \"advancement.integrateddynamics.variables.desc\": \"Maak een variabelekaart\",\n    \"advancement.integrateddynamics.variable_input\": \"Informatie transformatie\",\n    \"advancement.integrateddynamics.variable_input.desc\": \"Maak een variabele invoertransformator\",\n    \"advancement.integrateddynamics.variable_output\": \"Uit-informatie transformatie\",\n    \"advancement.integrateddynamics.variable_output.desc\": \"Maak een variabele uitvoertransformator\",\n    \"advancement.integrateddynamics.redstone_reading\": \"Redstonedetectie\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"Maak een redstonelezer\",\n    \"advancement.integrateddynamics.block_reading\": \"Blokdetectie\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"Maak een bloklezer\",\n    \"advancement.integrateddynamics.inventory_reading\": \"Inventarisdetectie\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"Maak een inventarislezer\",\n    \"advancement.integrateddynamics.machine_reading\": \"Machinedetectie\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"Maak een machinelezer\",\n    \"advancement.integrateddynamics.value_displaying\": \"Waardevolle weergave\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"Maak een weergavepaneel\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"Digitale redstone\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"Maak een variabelekaart voor een redstonewaarde van een §6geheel getal§7.\",\n    \"advancement.integrateddynamics.redstone_observement\": \"Visuele redstone\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"Plaats een §6geheel getal§7 variabelekaart in een weergavepaneel.\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"Redstonesimulatie\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"Laat een redstoneschrijver een maximaal geheel getal redstoneniveau schrijven dat door een redstonelezer werd gelezen.\",\n    \"advancement.integrateddynamics.recipe_reading\": \"Receptdetectie\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"Lees de lijst van recepten van een machine (zoals de werkbank) met behulp van een machinelezer.\",\n    \"advancement.integrateddynamics.recipe_creation\": \"Receptbeschrijving\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"Maak een §8recept§7 met behulp van de visuele logicaprogrammeur.\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"Receptnaslag\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"Zoek het recept van een kist dynamisch op met behulp van een machinelezer.\",\n    \"advancement.integrateddynamics.lectern_data\": \"Lessenaar NBT\",\n    \"advancement.integrateddynamics.lectern_data.desc\": \"Lees het NBT label van een machine met behulp van een bloklezer, zoals de lessenaar.\",\n    \"advancement.integrateddynamics.lectern_book\": \"Lessenaar NBT boek\",\n    \"advancement.integrateddynamics.lectern_book.desc\": \"Lees het NBT boekveld van een lessenaarblok uit het NBT label gelezen door een bloklezer.\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"Lessenaar NBT boeknaam\",\n    \"advancement.integrateddynamics.lectern_book_name.desc\": \"Lees de naam (ID) van een boek in een lessenaarblok door een NBT-pad uitdrukking toe te passen op het NBT label gelezen door een bloklezer.\",\n    \"advancement.integrateddynamics.logic_programming\": \"Programmeren met logica\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"Open de logicaprogrammeur GUI\",\n    \"advancement.integrateddynamics.constant_definition\": \"Constante definitie\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"Definiëer een variabelekaart voor een constant §6geheel getal§7.\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"Rekenkundig optellen\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"Tel twee §egetallen§7 bij elkaar op en geef het resultaat weer.\",\n    \"advancement.integrateddynamics.variable_materialization\": \"Variabele materialisatie\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"Materialiseer een variabele in een §lmaterialisator§r.\",\n    \"advancement.integrateddynamics.variable_proxying\": \"Verwijzen naar variabelen\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"Maak een geldige proxy variabele in een §lproxy§r.\",\n    \"advancement.integrateddynamics.logical_list_building\": \"Logische lijstopstelling\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"Bouw een statische lijst met de §6gehele getallen§7 1, 10 en 100.\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"Voorwerpoorsprong identificeren\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"Verkrijg de modnaam van een voorwerp.\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"Waar kijk ik nou naar?\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"Toon het blok waar je naar kijkt.\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"Dynamisch optellen\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"Roep de optellingsoperator dynamisch aan\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"Dynamische lijstfiltering\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"Filter een gehele getallenlijst van 1, 10 en 100 tot 10.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"Creeperdouane\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"Toon de GP van een Creeper in een weergavepaneel\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"Spons stap-geluid\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"Speel het stap-geluid van een natte sponsblok\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"Recursieve recursie\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"Maak een oneindige lijst, en toon het in een weergavepaneel\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"Gereedschap voor obsidiaan?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"Controleer of een stenen houweel obsidiaan kan breken, en toon het resultaat in een weergavepaneel.\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"Intelligente drukplaat\",\n    \"valuetype.integrateddynamics.error.value\": \"Ongeldige waarde\",\n    \"valuetype.integrateddynamics.value_type\": \"Waardetype\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§eType: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§oWaarde: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§eHandtekening: §r\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"Interpreteren van invoer \\\"%s\\\" voor deze waardetype mislukt.\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"Dit waardetype vereist een invoervoorwerp.\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"Lijstelement %s is ongeldig: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"De lijst verwacht waardetype %s terwijl %s werd opgegeven.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"Een operator-waardetype werd verwacht op positie %s, terwijl %s werd opgegeven\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"De operator waarde %s verwachtte waardetypen (%s), terwijl (%s) werd opgegeven.\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"Het ingevoegde voorwerp is geen blok.\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"Het ingevoegde voorwerp bevat geen vloeistof.\",\n    \"valuetype.integrateddynamics.error.no_light_calculator\": \"Het waardetype %s kan niet gebruikt worden om een lichtniveau af te leiden.\",\n    \"valuetype.integrateddynamics.error.deserialize\": \"Iets ging mis tijdens het deserialiseren van '%s': %s\",\n    \"valuetype.integrateddynamics.any\": \"Willekeurig\",\n    \"valuetype.integrateddynamics.any.info\": \"Accepteert ieder waardetype.\",\n    \"valuetype.integrateddynamics.number\": \"Nummer\",\n    \"valuetype.integrateddynamics.number.info\": \"Accepteert alle getaltypen.\",\n    \"valuetype.integrateddynamics.named\": \"Benoemd\",\n    \"valuetype.integrateddynamics.named.info\": \"Iets dat een naam heeft, zoals weergavenamen van voorwerpen en blokken.\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"Uniek benoemd\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"Iets dat een unieke naam heeft, zoals voorwerpen en blokken.\",\n    \"operator.integrateddynamics.string.string_error\": \"Fout\",\n    \"operator.integrateddynamics.string.string_error.info\": \"Veroorzaak een aangepaste fout\",\n    \"valuetype.integrateddynamics.boolean\": \"Booleaanse waarde\",\n    \"valuetype.integrateddynamics.integer\": \"Geheel getal\",\n    \"valuetype.integrateddynamics.double\": \"Dubbeleprecisiegetal\",\n    \"valuetype.integrateddynamics.long\": \"Lang geheel getal\",\n    \"valuetype.integrateddynamics.string\": \"Tekenreeks\",\n    \"valuetype.integrateddynamics.operator\": \"Operator\",\n    \"valuetype.integrateddynamics.nbt\": \"NBT\",\n    \"valuetype.integrateddynamics.list\": \"Lijst\",\n    \"valuetype.integrateddynamics.block\": \"Blok\",\n    \"valuetype.integrateddynamics.itemstack\": \"Voorwerp\",\n    \"valuetype.integrateddynamics.entity\": \"Entiteit\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Vloeistof\",\n    \"valuetype.integrateddynamics.ingredients\": \"Ingrediënten\",\n    \"valuetype.integrateddynamics.recipe\": \"Recept\",\n    \"valuetype.integrateddynamics.ingredients.slot.info\": \"Shift-klik om te configureren\",\n    \"parttype.integrateddynamics.error.low_energy\": \"Niet genoeg energie\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"Energie is op\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"Inactief\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"Fouten:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"Aspect: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"Waarde: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"Groep: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"WAARSCHUWING: Geen aspecten beschikbaar. Dit kan bruikbaar worden wanneer andere mods zijn geïnstalleerd.\",\n    \"parttype.integrateddynamics.audio_reader\": \"Geluidlezer\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"Leest geluidsinformatie\",\n    \"parttype.integrateddynamics.block_reader\": \"Bloklezer\",\n    \"parttype.integrateddynamics.block_reader.info\": \"Leest blokinformatie\",\n    \"parttype.integrateddynamics.entity_reader\": \"Entiteitlezer\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"Leest entiteitsinformatie\",\n    \"parttype.integrateddynamics.fluid_reader\": \"Vloeistoflezer\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"Leest vloeistoftanken\",\n    \"parttype.integrateddynamics.inventory_reader\": \"Inventarislezer\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"Leest gegevens van inventarissen\",\n    \"parttype.integrateddynamics.network_reader\": \"Netwerklezer\",\n    \"parttype.integrateddynamics.network_reader.info\": \"Leest doelwit netwerkeigenschappen\",\n    \"parttype.integrateddynamics.machine_reader\": \"Machinelezer\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"Leest machine-eigenschappen\",\n    \"parttype.integrateddynamics.redstone_reader\": \"Redstonelezer\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"Leest redstonewaardes\",\n    \"parttype.integrateddynamics.world_reader\": \"Wereldlezer\",\n    \"parttype.integrateddynamics.world_reader.info\": \"Leest dingen gerelateerd aan de wereld, blokken en entiteiten\",\n    \"parttype.integrateddynamics.audio_writer\": \"Geluidschrijver\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"Produceert geluid en muziek\",\n    \"parttype.integrateddynamics.effect_writer\": \"Effectschrijver\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"Produceert deeltjes\",\n    \"parttype.integrateddynamics.entity_writer\": \"Entiteitschrijver\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"Schrijft entiteitconfiguratie\",\n    \"parttype.integrateddynamics.machine_writer\": \"Machineschrijver\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"Schrijft machineconfiguratie\",\n    \"parttype.integrateddynamics.inventory_writer\": \"Inventarisschrijver\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"Schrijft naar inventarissen\",\n    \"parttype.integrateddynamics.redstone_writer\": \"Redstoneschrijver\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"Schrijft naar redstone\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"Dit paneel accepteert geen %s.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"Statisch lichtpaneel\",\n    \"parttype.integrateddynamics.static_light_panel.info\": \"Een eenvoudig lichtpaneel dat altijd actief is\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"Dynamisch lichtpaneel\",\n    \"parttype.integrateddynamics.dynamic_light_panel.info\": \"Toon een variabele waarde in de wereld als een visuele lichtwaarde\",\n    \"parttype.integrateddynamics.display_panel\": \"Weergavepaneel\",\n    \"parttype.integrateddynamics.display_panel.info\": \"Toon een variabele waarde in de wereld als visuele tekst\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"Rechtelijnsverbindingsstuk\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"Kan verbinding maken met andere rechtelijnsverbindingsstukken over een afstand in een rechte lijn\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"Richtingloos verbindingsstuk\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"Kan van overal vandaan verbinding maken met andere richtingloze verbindingsstukken, inclusief in andere dimensies.\",\n    \"variable.integrateddynamics.error.invalid_item\": \"De huidige variabele kan niet gebruikt worden in dit aspect.\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"Aanzetstuk met ID %s niet gevonden binnen het huidige netwerk.\",\n    \"variable.integrateddynamics.error.recursion\": \"De variabele met ID %s verwijst naar zichzelf.\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§oProxy ID: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"Proxy met ID %s kon niet gevonden worden binnen het huidige netwerk.\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"Proxy met ID %s verstrekt geen variabele.\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"Proxy met ID %s wordt verwacht een variabele te verstrekken van het type %s terwijl %s werd gevonden.\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§eAspect: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§eType: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§oAanzetstuk-ID: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"Dit aspect verwacht %s, maar %s werd opgegeven.\",\n    \"aspect.integrateddynamics.error.recursion\": \"De aspectvariabele voor %s verwijst naar zichzelf.\",\n    \"aspect.integrateddynamics.error.no_value_interface\": \"Geen geldige waarde-interface gevonden.\",\n    \"aspect.integrateddynamics.error.no_value_interface_value\": \"Geen geldige waarde voor de waarde-interface gevonden.\",\n    \"aspect.integrateddynamics.name\": \"Aspect\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"Geluid\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"Speel het geluid met de gegeven naam\",\n    \"aspect.integrateddynamics.write.string.audio.text\": \"Tekst\",\n    \"aspect.integrateddynamics.write.string.audio.text.info\": \"Converteer de gegeven tekst naar spraak\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"Heeft blok\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"Of het doelwit een blok bevat\",\n    \"aspect.integrateddynamics.read.string.block.dimension\": \"Dimensie\",\n    \"aspect.integrateddynamics.read.string.block.dimension.info\": \"Het doelwit wereldimensie-ID\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"X-coördinaat\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"Het x-coördinaat van het doelwit\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Y-coördinaat\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"Het y-coördinaat van het doelwit\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Z-coördinaat\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"Het z-coördinaat van het doelwit\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"Blokentiteit NBT\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"De doelblokentiteit als NBT\",\n    \"aspect.integrateddynamics.read.string.block.biome\": \"Klimaat\",\n    \"aspect.integrateddynamics.read.string.block.biome.info\": \"De klimaatnaam van het doelwit\",\n    \"aspect.integrateddynamics.read.integer.block.light\": \"Lichtniveau\",\n    \"aspect.integrateddynamics.read.integer.block.light.info\": \"Het lichtniveau op het doelwit\",\n    \"aspect.integrateddynamics.read.block.block\": \"Blok\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"Het doelblok\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"Voorwerplijst rotatie\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"De index van de rotatie die het voorwerp heeft in de gekoppelde voorwerplijst.\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"Entiteiten\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"De entiteiten in het ruimte van het doelwit\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"Spelers\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"De spelers in de ruimte van het doelwit\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"Entiteit\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"De geselecteerde entiteit in de ruimte van het doelwit\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"Voorwerplijst\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"Het voorwerp in een voorwerplijst gekoppeld aan de lezer\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"Willekeurig\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"Een willekeurig geheel getal\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"Aantal spelers\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"Het aantal spelers in de server\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"Ticktijd\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"De gemiddelde ticktijd in nanoseconden voor de server\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"Het gemiddelde aantal ticks per seconde in alle werelden\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"Spelers\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"De spelers die online zijn op deze server\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"Tank leeg\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"Of het vloeistoftankdoelwit leeg is\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"Tank vol\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"Of het vloeistoftankdoelwit vol is\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"Tank niet leeg\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"Of er iets in het vloeistoftankdoelwit zit\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"Is tank\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"Of het doelwit een vloeistoftank heeft\",\n    \"aspect.integrateddynamics.read.long.fluid.amount\": \"Vloeistofhoeveelheid\",\n    \"aspect.integrateddynamics.read.long.fluid.amount.info\": \"De hoeveelheid vloeistof in mB in de actieve tank\",\n    \"aspect.integrateddynamics.read.long.fluid.totalamount\": \"Totale vloeistofhoeveelheid\",\n    \"aspect.integrateddynamics.read.long.fluid.totalamount.info\": \"De totale hoeveelheid vloeistof in mB\",\n    \"aspect.integrateddynamics.read.long.fluid.capacity\": \"Vloeistofcapaciteit\",\n    \"aspect.integrateddynamics.read.long.fluid.capacity.info\": \"Totale capaciteit van de actieve tank\",\n    \"aspect.integrateddynamics.read.long.fluid.totalcapacity\": \"Totale vloeistofcapaciteit\",\n    \"aspect.integrateddynamics.read.long.fluid.totalcapacity.info\": \"Totale capaciteit van de tank\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"Tanken\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"Het aantal tanken gevonden in het doelblok\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"Vloeistof vulverhouding\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"De hoeveelheid vloeistof in de actieve tank gedeeld door zijn capaciteit\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"Tankvloeistoffen\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"De lijst van vloeistoffen in alle tanken\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"Tankcapaciteiten\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"De lijst van capaciteiten van alle tanken\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"Tankvloeistof\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"De vloeistof in de doeltank\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"Blokvloeistof\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"De vloeistof in het doelblok\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"Inventaris vol\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"Of alle vakken ten minste één voorwerp hebben\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"Inventaris leeg\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"Of alle vakken leeg zijn\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"Inventaris niet leeg\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"Of er ten minste één voorwerp is\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"Is inventaris\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"Of het doelwit een inventaris heeft\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"Inventaristelling\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"Krijg het totale aantal voorwerpen\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"Vak stapelgrootte\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"Krijg de stapelgrootte van het actieve vak\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"Vakken\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"Het aantal vakken in de inventaris\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"Vakken gevuld\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"Het aantal vakken dat een voorwerp heeft\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"Vulverhouding\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"Het aantal niet-lege vakken gedeeld door het totale aantal vakken\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"Voorwerpen\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"Krijg een lijst van voorwerpen in de inventaris\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"Vak voorwerp\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"Krijg het voorwerp in het actieve vak\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"Is arbeidsmachine\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"Of het doelwit een arbeidsmachine is\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"Heeft werk\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"Of het doelwit werk te verrichten heeft\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"Kan werken\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"Of het doelwit werk zou kunnen starten in zijn huidige staat\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"Is bezig\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"Of het doelwit bezig is met werk\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"Heeft temperatuur\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"Of het doelwit een bepaalde temperatuur heeft\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"Temperatuur\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature.info\": \"De huidige temperatuur van het doelwit in Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"Maximumtemperatuur\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature.info\": \"De maximale temperatuur van het doelwit in Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"Minimumtemperatuur\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature.info\": \"De minimale temperatuur van het doelwit in Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"Standaard temperatuur\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature.info\": \"De standaardtemperatuur van het doelwit in Kelvin\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"Is receptverwerker\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"Of het doelwit recepten kan verwerken\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"Recepten\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"Recepten die verwerkt kunnen worden door het doelwit\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"Recept uitvoer per invoer\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"Een operator die de resulterende receptuitvoer teruggeeft voor een gegeven invoer.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"Recept invoer per uitvoer\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"Een operator die alle mogelijke receptinvoer teruggeeft voor een gegeven uitvoer.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"Recepten per invoer\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"Een operator die alle mogelijke recepten teruggeeft voor een gegeven invoer.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"Recepten per uitvoer\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"Een operator die alle mogelijke recepten teruggeeft voor een gegeven uitvoer.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"Recept per invoer\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"Een operator die een recept teruggeeft voor een gegeven invoer.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"Recept per uitvoer\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"Een operator die een recept teruggeeft voor een gegeven uitvoer.\",\n    \"aspect.integrateddynamics.read.boolean.energy.applicable\": \"Is energieverwerker\",\n    \"aspect.integrateddynamics.read.boolean.energy.applicable.info\": \"Of het doelwit op een of andere manier energie kan verwerken\",\n    \"aspect.integrateddynamics.read.boolean.energy.canextract\": \"Kan energie afgeven\",\n    \"aspect.integrateddynamics.read.boolean.energy.canextract.info\": \"Of energie daadwerkelijk afgegeven kan worden door het doelwit, houdt rekening met opslag\",\n    \"aspect.integrateddynamics.read.boolean.energy.caninsert\": \"Accepteert energie\",\n    \"aspect.integrateddynamics.read.boolean.energy.caninsert.info\": \"Of energie daadwerkelijk geaccepteerd wordt door het doelwit, houdt rekening met opslag en capaciteit\",\n    \"aspect.integrateddynamics.read.boolean.energy.isfull\": \"Is energiebuffer vol\",\n    \"aspect.integrateddynamics.read.boolean.energy.isfull.info\": \"Of de energiebuffer van het doelwit volledig vol is\",\n    \"aspect.integrateddynamics.read.boolean.energy.isempty\": \"Is energiebuffer leeg\",\n    \"aspect.integrateddynamics.read.boolean.energy.isempty.info\": \"Of de energiebuffer van het doelwit volledig leeg is\",\n    \"aspect.integrateddynamics.read.boolean.energy.isnonempty\": \"Is energiebuffer niet leeg\",\n    \"aspect.integrateddynamics.read.boolean.energy.isnonempty.info\": \"Of de energiebuffer van het doelwit niet leeg is\",\n    \"aspect.integrateddynamics.read.long.energy.amount\": \"Opgeslagen energie\",\n    \"aspect.integrateddynamics.read.long.energy.amount.info\": \"De hoeveelheid opgeslagen energie in het doelwit\",\n    \"aspect.integrateddynamics.read.long.energy.capacity\": \"Energiecapaciteit\",\n    \"aspect.integrateddynamics.read.long.energy.capacity.info\": \"De energiecapaciteit van het doelwit\",\n    \"aspect.integrateddynamics.read.double.energy.fillratio\": \"Energievulverhouding\",\n    \"aspect.integrateddynamics.read.double.energy.fillratio.info\": \"De hoeveelheid energie in het doelwit gedeeld door zijn capaciteit\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"Is netwerk\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"Of het doelwit een netwerk heeft\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"Onderdelen\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"Het aantal onderdelen in het doelnetwerk\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"Energiebatterijen\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"Het aantal energiebatterijen in het doelnetwerk\",\n    \"aspect.integrateddynamics.read.long.network.energy.stored\": \"Opgeslagen energie\",\n    \"aspect.integrateddynamics.read.long.network.energy.stored.info\": \"De hoeveelheid beschikbare energie in het doelnetwerk\",\n    \"aspect.integrateddynamics.read.long.network.energy.max\": \"Energiecapaciteit\",\n    \"aspect.integrateddynamics.read.long.network.energy.max.info\": \"De energiecapaciteit van het netwerk\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"Energieverbruik\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"De mate van energieverbruik van alle ID-onderdelen binnen het doelnetwerk per tick\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"Waarde\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"De waarde dat wordt verstrekt door het doelwit\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"Redstone hoog\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"Of het redstoneniveau maximaal is\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"Redstone laag\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"Of er geen redstoneniveau is\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"Redstone\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"Of er een redstoneniveau is\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"Redstoneklok\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"Pulst met een configureerbare lengte en interval.\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"Redstone\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"Krijg het exacte redstoneniveau\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"Vergelijker\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"Krijg de vergelijkerwaarde\",\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"Redstone\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"Produceert een vol redstonesignaal indien waar\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"Redstonepuls\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"Produceert een puls met de geconfigureerde redstonewaarde wanneer de gegeven waarde verandert.\",\n    \"aspect.integrateddynamics.write.integer.redstone\": \"Redstone\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"Produceert de waarde als exact redstonesignaal\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"Redstonepuls\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"Produceert een puls met de geconfigureerde redstonewaarde wanneer de gegeven waarde verandert.\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"Heeft entiteit\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"Of de ruimte van het doelwit een entiteit heeft\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"Heeft wezen\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"Of de ruimte van het doelwit een wezen heeft\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"Heeft dier\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"Of de ruimte van het doelwit een dier heeft\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"Heeft speler\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"Of de ruimte van het doelwit een speler heeft\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"Heeft voorwerp\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"Of de ruimte van het doelwit een voorwerp heeft\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"Weer: helder\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"Of er helder weer in de wereld is\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"Weer: regen\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining.info\": \"Of het regent in de wereld\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"Weer: onweer\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"Of er een onweersbui in de wereld is\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"Is dag\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"Of het dag is in de doelwereld\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"Is nacht\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"Of het nacht is in de doelwereld\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"Entiteiten\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"Het aantal entiteiten in de ruimte van het doelwit\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"Regen aftellen\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"Het aantal ticks totdat het gaat regenen in deze wereld\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"Ticktijd\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"De gemiddelde ticktijd in milliseconden voor deze wereld\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"Dagtijd\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"De tijd die gedurende de dag verstreken is in de doelwereld\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"Lichtniveau\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"Het lichtniveau op het doelwit\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"Aantal spelers\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"De spelers die online zijn in deze wereld\",\n    \"aspect.integrateddynamics.read.double.world.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"Het aantal ticks per seconde in deze wereld\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"Tijd\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"De wereldtijd afhankelijk van de tijdverzetopdracht\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"Totale tijd\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"De wereldtijd onafhankelijk van de tijdverzetopdracht\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"Wereldnaam\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"De naam van de doelwereld\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"Spelers\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"De spelers die in deze wereld zijn\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"Tank-ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"Vak-ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"Lijstelement\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"Bereik\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"Kanaal\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"Pulsinterval\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"Pulslengte\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"Volume\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"Frequentie\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"Aantal deeltjes\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"Krachtsignaal\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"Of een redstonefakkel gesimuleerd moet worden in plaats van gewoon redstonestof\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"Pulswaarde\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"De pulswaarde die uitgezonden moet worden.\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_length\": \"Pulslengte\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_length.info\": \"De duur van de puls in ticks.\",\n    \"operator.integrateddynamics\": \"Operator\",\n    \"operator.integrateddynamics.applied.operator_name\": \"Toegepast %s [%s]\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§eOperator: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§eCategorie: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§eVariabele IDs: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§eInvoertype %s: §r\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§eUitvoertype: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"De operator %s ontving een invoer van lengte %s terwijl hij een lengte van %s vereist.\",\n    \"operator.integrateddynamics.error.operator_param_wrong_input_length\": \"Een operatorinvoerlengte van %s is vereist, terwijl %s alleen een invoerlengte heeft van %s.\",\n    \"operator.integrateddynamics.error.null_type\": \"De operator %s ontving een invoer van een nul-variabele op positie %s.\",\n    \"operator.integrateddynamics.error.wrong_type\": \"De operator %s ontving een invoer van type %s op positie %s terwijl het type %s werd verwacht.\",\n    \"operator.integrateddynamics.error.wrong_type_output\": \"De evaluatie voor operator %s gaf %s terug in plaats van de verwachte %s.\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"Variabele met ID %s niet gevonden binnen het huidige netwerk.\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"Variabele met ID %s hangt af van zichzelf, oneindige recursie.\",\n    \"operator.integrateddynamics.error.operator_nbt_path_expression\": \"Een ongeldige NBT-pad uitdrukking '%s' gevonden: %s\",\n    \"operator.integrateddynamics.error.divide_by_zero\": \"Delen door nul is niet toegestaan.\",\n    \"operator.integrateddynamics.error.operator_not_found\": \"Kon geen operator vinden met naam \\\"%s\\\".\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"Optelling\",\n    \"operator.integrateddynamics.integer.basename\": \"Geheel getal %s\",\n    \"operator.integrateddynamics.string.basename\": \"Tekenreeks %s\",\n    \"operator.integrateddynamics.string.length\": \"Lengte\",\n    \"operator.integrateddynamics.string.length.info\": \"De lengte van de gegeven tekenreeks\",\n    \"operator.integrateddynamics.string.unique_name\": \"Unieke naam\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"Krijg de unieke naam van een object\",\n    \"operator.integrateddynamics.list\": \"Lijst\",\n    \"operator.integrateddynamics.list.basename\": \"Lijst %s\",\n    \"operator.integrateddynamics.list.length\": \"Lengte\",\n    \"operator.integrateddynamics.list.length.info\": \"De lengte van de gegeven lijst\",\n    \"operator.integrateddynamics.list.empty\": \"Leeg\",\n    \"operator.integrateddynamics.list.empty.info\": \"Of the lijst leeg is\",\n    \"operator.integrateddynamics.list.notempty\": \"Niet leeg\",\n    \"operator.integrateddynamics.list.notempty.info\": \"Of de lijst niet leeg is\",\n    \"operator.integrateddynamics.list.contains\": \"Bevat\",\n    \"operator.integrateddynamics.list.contains.info\": \"Of de lijst het gegeven element bevat.\",\n    \"operator.integrateddynamics.list.contains_p\": \"Bevat-predicaat\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"Of ten minste één van de elementen uit de lijst waar geeft voor de gegeven predicaat.\",\n    \"operator.integrateddynamics.list.count\": \"Tellen\",\n    \"operator.integrateddynamics.list.count.info\": \"Het aantal keer dat het gegeven element in de lijst wordt gevonden.\",\n    \"operator.integrateddynamics.list.count_p\": \"Tellen-predicaat\",\n    \"operator.integrateddynamics.list.count_p.info\": \"Het aantal keer dat het gegeven predicaat waar geeft voor de elementen in de lijst.\",\n    \"operator.integrateddynamics.list.uniq_p\": \"Uniek-predicaat\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"Verwijder alle dubbele elementen uit de lijst, gebaseerd op het gegeven vergelijkingspredicaat.\",\n    \"operator.integrateddynamics.list.uniq\": \"Uniek\",\n    \"operator.integrateddynamics.list.uniq.info\": \"Verwijder alle dubbele elementen uit de lijst.\",\n    \"operator.integrateddynamics.block\": \"Blok\",\n    \"operator.integrateddynamics.block.basename\": \"Blok %s\",\n    \"operator.integrateddynamics.block.stepsound\": \"Blok stap-geluid\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"Het stap-geluid van het gegeven blok\",\n    \"operator.integrateddynamics.itemstack\": \"Voorwerp\",\n    \"operator.integrateddynamics.itemstack.basename\": \"Voorwerp %s\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"Zeldzaamheid\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"De zeldzaamheid van het voorwerp\",\n    \"operator.integrateddynamics.itemstack.block\": \"Blok\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"Het blok van het gegeven voorwerp\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"Heeft vloeistof\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"Of het gegeven voorwerp een vloeistof bevat\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"Vloeistof\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"De vloeistof in het gegeven voorwerp\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"Vloeistofcapaciteit\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"De vloeistofcapaciteit van het gegeven voorwerp in mB\",\n    \"operator.integrateddynamics.itemstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"De naam van de mod van het gegeven voorwerp\",\n    \"operator.integrateddynamics.entity\": \"Entiteit\",\n    \"operator.integrateddynamics.entity.basename\": \"Entiteit %s\",\n    \"operator.integrateddynamics.entity.ismob\": \"Is wezen\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"Of de entiteit een wezen is\",\n    \"operator.integrateddynamics.entity.isanimal\": \"Is dier\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"Of de entiteit een dier is\",\n    \"operator.integrateddynamics.entity.isitem\": \"Is voorwerp\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"Of de entiteit een voorwerp is\",\n    \"operator.integrateddynamics.entity.isplayer\": \"Is speler\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"Of de entiteit een speler is\",\n    \"operator.integrateddynamics.entity.isminecart\": \"Is mijnkar\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"Of de entiteit een mijnkar is\",\n    \"operator.integrateddynamics.entity.health\": \"Gezondheid\",\n    \"operator.integrateddynamics.entity.health.info\": \"De entiteitgezondheid\",\n    \"operator.integrateddynamics.entity.width\": \"Breedte\",\n    \"operator.integrateddynamics.entity.width.info\": \"De entiteitbreedte\",\n    \"operator.integrateddynamics.entity.height\": \"Hoogte\",\n    \"operator.integrateddynamics.entity.height.info\": \"De entiteithoogte\",\n    \"operator.integrateddynamics.entity.isburning\": \"Is in brand\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"Of de entiteit in brand is\",\n    \"operator.integrateddynamics.entity.iswet\": \"Is nat\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"Of de entiteit nat is\",\n    \"operator.integrateddynamics.entity.iseating\": \"Is aan het eten\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"Of de entiteit aan het eten is\",\n    \"operator.integrateddynamics.entity.mod\": \"Mod\",\n    \"operator.integrateddynamics.entity.mod.info\": \"De naam van de mod van de gegeven entiteit\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"Entiteitvloeistoffen\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"De vloeistoffen die in deze entiteit zitten.\",\n    \"operator.integrateddynamics.itemstack.isenergycontainer\": \"Is energieopslag\",\n    \"operator.integrateddynamics.itemstack.isenergycontainer.info\": \"Of het gegeven voorwerp energie kan opslaan\",\n    \"operator.integrateddynamics.itemstack.storedenergy\": \"Opgeslagen energie\",\n    \"operator.integrateddynamics.itemstack.storedenergy.info\": \"De hoeveelheid opgeslagen energie in dit voorwerp\",\n    \"operator.integrateddynamics.itemstack.energycapacity\": \"Energiecapaciteit\",\n    \"operator.integrateddynamics.itemstack.energycapacity.info\": \"De maximale hoeveelheid energie die kan worden opgeslagen in dit voorwerp\",\n    \"operator.integrateddynamics.fluidstack\": \"Vloeistof\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"Vloeistof %s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"Hoeveelheid\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"De hoeveelheid in mB\",\n    \"operator.integrateddynamics.fluidstack.block\": \"Blok\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"Het vloeistofblok\",\n    \"operator.integrateddynamics.fluidstack.light_level\": \"Lichtniveau\",\n    \"operator.integrateddynamics.fluidstack.light_level.info\": \"Het lichtniveau dat de vloeistof uitzendt\",\n    \"operator.integrateddynamics.fluidstack.density\": \"Dichtheid\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"De vloeistofdichtheid\",\n    \"operator.integrateddynamics.fluidstack.temperature\": \"Temperatuur\",\n    \"operator.integrateddynamics.fluidstack.temperature.info\": \"De vloeistoftemperatuur\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"Viscositeit\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"De vloeistofviscositeit\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air\": \"Is lichter dan lucht\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air.info\": \"Of de vloeistof lichter is dan lucht\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"Zeldzaamheid\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"De vloeistofzeldzaamheid\",\n    \"operator.integrateddynamics.fluidstack.bucket\": \"Emmer\",\n    \"operator.integrateddynamics.fluidstack.bucket.info\": \"De gevulde emmer voor de vloeistof\",\n    \"operator.integrateddynamics.fluidstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"De naam van de mod van de gegeven vloeistof\",\n    \"operator.integrateddynamics.fluidstack.with_amount\": \"Vloeistof met hoeveelheid\",\n    \"operator.integrateddynamics.fluidstack.with_amount.info\": \"Kopiëer de gegeven vloeistof met de gegeven hoeveelheid\",\n    \"operator.integrateddynamics.operator\": \"Operator\",\n    \"operator.integrateddynamics.operator.basename\": \"Operator %s\",\n    \"operator.integrateddynamics.recipe\": \"Recept\",\n    \"operator.integrateddynamics.recipe.basename\": \"Recept %s\",\n    \"info_book.integrateddynamics.section.main\": \"Over de Dynamiek van Integratie\",\n    \"info_book.integrateddynamics.tag_index\": \"&oIndex&r\",\n    \"info_book.integrateddynamics.introduction\": \"Introductie\",\n    \"info_book.integrateddynamics.tutorials\": \"Lessen\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"Introductie\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"Menrilbeginselen\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"Netwerkfundamenten\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"Basis netwerkcomponenten\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"Aspecten\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"Logische bewerkingen\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"Receptverwerking\",\n    \"info_book.integrateddynamics.tutorials.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"Geavanceerde netwerkcomponenten\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"Geavanceerde bewerkingen\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"De waarde van operators\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"Uitdagingen\",\n    \"info_book.integrateddynamics.manual\": \"Handleiding\",\n    \"info_book.integrateddynamics.manual.introduction\": \"Introductie\",\n    \"info_book.integrateddynamics.manual.introduction.text1\": \"Dit gedeelte van het boek bevat gedetailleerde beschrijvingen van alle onderdelen van de mod.\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"Eerste stappen\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"Menrilbomen\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"Samenperser\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"Droger\",\n    \"info_book.integrateddynamics.manual.networks\": \"Netwerken\",\n    \"info_book.integrateddynamics.manual.logic\": \"Logica\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"Waardetypen\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean\": \"Booleaanse waarde\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer\": \"Geheel getal\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double\": \"Dubbeleprecisiegetal\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long\": \"Lang geheel getal\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string\": \"Tekenreeks\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list\": \"Lijst\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator\": \"Operator\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block\": \"Blok\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"Voorwerp\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"Entiteit\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"Vloeistof\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"Ingrediënten\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"Recept\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"Variabelen\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"Variabelekaart\",\n    \"info_book.integrateddynamics.manual.logic.advanced\": \"Geavanceerd\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path\": \"NBT-pad\",\n    \"info_book.integrateddynamics.manual.parts\": \"Aanzetstukken\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"Introductie\",\n    \"info_book.integrateddynamics.manual.parts.settings\": \"Instellingen\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"Lezers\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"Introductie\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"Geluid\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"Informatie over muziek en geluid dat afspeelt in de omgeving.\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"Blok\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"Entiteit\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"Informatie over de server, zoals de lijst van online spelers.\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"Vloeistof\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"Inventaris\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"Machine\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"Netwerk\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"Redstone\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"Wereld\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"Informatie over de wereld waarin dit aanzetstuk zich bevindt, zoals het huidige weer en de tijd.\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"Schrijvers\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"Geluid\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"Speel geluiden en muziek.\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"Effect\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"Stoot deeltjes uit op basis van bepaalde waarden en instellingen.\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"Entiteit\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"Configureer en bestuur entiteiten.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"Machine\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"Configureer en bestuur machines.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"Inventaris\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"Acties uitvoeren gerelateerd aan inventarissen en buizen. Dit zal zelf geen voorwerpen overdragen.\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"Redstone\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"Produceer een bepaald redstoneniveau.\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"Overig\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"Licht\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"Weergavepaneel\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"Rechtelijnsverbindingsstuk\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"Soms zullen je logicanetwerken lange afstanden overbruggen. Ook al zijn gewone logicakabels daarvoor te gebruiken, het is wellicht niet erg handig om kabels over lange afstanden te plaatsen.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"Met &lrechtelijnsverbindingsstukken&r kun je netwerken draadloos over lange afstanden verbinden. Plaats hiervoor eerst een verbindingsstuk op het ene netwerk, en een tweede op het andere netwerk. Deze verbindingsstukken moeten in een rechte lijn staan tegenover elkaar, op een afstand van maximaal 512 blokken. De verbindingsstukken worden visueel actief wanneer een geldige verbinding is gemaakt.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"Richtingloos verbindingsstuk\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"Hoewel &lrechtelijnsverbindingsstukken&r handig zijn voor langeafstandsverbindingen in een rechte lijn, kan dit in sommige gevallen een beetje te beperkt zijn.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"Je zou bijvoorbeeld meer dan twee netwerken aan elkaar willen kunnen verbinden door gebruik te maken van een enkel verbindingsstuk. Of je wilt netwerken uit verschillende dimensies met elkaar verbinden. In deze gevallen kun je de &lrichtingloze verbindingsstukken&r gebruiken.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"Deze verbindingsstukken zullen een uniek groep-ID krijgen op het moment van vervaardiging, welke de groep identificeert waarin netwerken verbonden kunnen worden. Elke verbindingsstukgroep heeft een unieke kleur van deeltjes wanneer in de wereld geplaatst. De groep-ID wordt weergegeven in de voorwerptekstballon of door te rechts-klikken op het verbindingsstuk wanneer geplaatst. Extra verbindingsstukken kunnen toegevoegd worden aan een groep door een nieuwe te maken gebruik makend van een bestaand verbindingsstuk binnen een groep.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"Je kunt ook de groep van bestaande verbindingsstukken op twee manieren wijzigen. Het vervaardigen van een enkel verbindingsstuk op zichzelf in een werkbank zal het een nieuwe groep toekennen zonder andere leden, en twee met elkaar vervaardigen zal de groep van de eerste kopiëren naar de tweede.\",\n    \"info_book.integrateddynamics.manual.machines\": \"Machines\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"Materialisator\",\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"Proxy\",\n    \"info_book.integrateddynamics.manual.machines.delay\": \"Vertrager\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"Energiebatterij\",\n    \"info_book.integrateddynamics.manual.machines.generator\": \"Generator\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"Mechanische samenperser\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"Mechanische droger\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"Logicaprogrammering\",\n    \"info_book.integrateddynamics.manual.diagnostics\": \"Netwerkdiagnostiek\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/no_no.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/pl_pl.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/pt_br.json",
    "content": "{\n    \"_comment\": \"------ On the Dynamics of Integration contents ------\",\n    \"itemGroup.integrateddynamics\": \"Integrated Dynamics\",\n    \"general.integrateddynamics.description\": \"Assuma o controle total e automatizado de suas máquinas.\",\n    \"general.integrateddynamics.info.none\": \"Nenhum\",\n    \"general.integrateddynamics.energy\": \"Energia\",\n    \"general.integrateddynamics.energy_unit\": \"RF\",\n    \"general.integrateddynamics.error.no_network\": \"Nenhuma rede válida encontrada\",\n    \"general.integrateddynamics.true\": \"Verdadeiro\",\n    \"general.integrateddynamics.false\": \"Falso\",\n    \"gui.integrateddynamics.part_settings\": \"Configurações da Peça\",\n    \"gui.integrateddynamics.aspect_settings\": \"Configurações de Aspecto\",\n    \"gui.integrateddynamics.part_offsets\": \"Deslocamentos da Peça\",\n    \"gui.integrateddynamics.keybinding\": \"Atalho de Teclado\",\n    \"gui.integrateddynamics.button.save\": \"Salvar\",\n    \"gui.integrateddynamics.button.rename\": \"Renomear\",\n    \"gui.integrateddynamics.button.copy\": \"Copiar\",\n    \"gui.integrateddynamics.button.copy.info\": \"Copiar para a área de transferência\",\n    \"gui.integrateddynamics.button.edit\": \"Editar\",\n    \"gui.integrateddynamics.button.add\": \"Adicionar\",\n    \"gui.integrateddynamics.button.remove\": \"Remover\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"Ticks/Operação\",\n    \"gui.integrateddynamics.partsettings.priority\": \"Prioridade\",\n    \"gui.integrateddynamics.partsettings.channel\": \"Canal de Energia\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"O consumo de energia da rede está desativado neste servidor.\",\n    \"gui.integrateddynamics.partsettings.side\": \"Lado Alvo\",\n    \"gui.integrateddynamics.partoffset.offsets\": \"Deslocamento relativo à posição do alvo\",\n    \"gui.integrateddynamics.partoffset.offsets.max\": \"Deslocamento máximo: %s\",\n    \"gui.integrateddynamics.partoffset.offsets.max.howtoincrease\": \"Aumente o limite com a Melhoria de Peça: Deslocamento\",\n    \"gui.integrateddynamics.partoffset.slot.message.noint\": \"Coordenada inteira inválida\",\n    \"gui.integrateddynamics.partoffset.slot.message.outofrange\": \"O deslocamento excede o alcance permitido\",\n    \"gui.integrateddynamics.partoffset.x\": \"Deslocamento X\",\n    \"gui.integrateddynamics.partoffset.y\": \"Deslocamento Y\",\n    \"gui.integrateddynamics.partoffset.z\": \"Deslocamento Z\",\n    \"gui.integrateddynamics.delay.capacity\": \"Capacidade\",\n    \"gui.integrateddynamics.part.properties\": \"Propriedades:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"Filtro\",\n    \"gui.integrateddynamics.logicprogrammer.reset\": \"Limpar\",\n    \"gui.integrateddynamics.logicprogrammer.info.create\": \"§lVincular novo§r Cartão de Variável\",\n    \"gui.integrateddynamics.logicprogrammer.info.modify\": \"§lModificar§r Cartão de Variável\",\n    \"gui.integrateddynamics.logicprogrammer.tooltip.writeslot.create\": \"Insira um Cartão de Variável para vincular a um §lnovo§r valor\",\n    \"gui.integrateddynamics.logicprogrammer.tooltip.writeslot.modify\": \"Insira um Cartão de Variável para §lcarregar§r seu valor\",\n    \"gui.integrateddynamics.rewards\": \"Recompensas\",\n    \"gui.integrateddynamics.rewards.collect\": \"Coletar\",\n    \"gui.integrateddynamics.rewards.collected\": \"Coletado\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"Não foi possível coletar as recompensas, seu inventário está cheio?\",\n    \"gui.integrateddynamics.input\": \"Entrada: %s\",\n    \"gui.integrateddynamics.output\": \"Saída: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"Ejetar Fluido Automaticamente\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Ejetar Fluido Automaticamente: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"Empurra para tanques adjacentes.\",\n    \"gui.integrateddynamics.input_short\": \"Entrada\",\n    \"gui.integrateddynamics.output_short\": \"Saída\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"NBT Estrito\",\n    \"gui.integrateddynamics.recipe.tagvariants\": \"Variantes de tag de item\",\n    \"gui.integrateddynamics.recipe.reusable\": \"Reutilizável\",\n    \"gui.integrateddynamics.operator.globalname\": \"Nome global: %s\",\n    \"gui.integrateddynamics.operator.localname\": \"Nome do método: %s\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Integrated Dynamics - Diagnóstico de Rede\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"Peças\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"Observadores de Armazenamento\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Rede\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Cabos\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"Peça\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Tempo de Tick (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"Dimensão\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"Posição\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"Lado\",\n    \"gui.integrateddynamics.diagnostics.table.actions\": \"Ações\",\n    \"key.categories.integrateddynamics\": \"Integrated Dynamics\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"Focar Busca no Programador Lógico\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"Renomear Variável no Programador Lógico\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"Virar Página\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"Virar Páginas\",\n    \"block.integrateddynamics.cable\": \"Cabo Lógico\",\n    \"block.integrateddynamics.variablestore\": \"Armazenador de Variáveis\",\n    \"block.integrateddynamics.energy_battery\": \"Bateria de Energia\",\n    \"block.integrateddynamics.energy_battery.info\": \"Coloque na grade de criação com outras Baterias de Energia para aumentar a capacidade. Shift + Clique com o botão direito para auto-fornecimento.\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"Auto-fornecimento: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"Bateria de Energia Criativa\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"Auto-fornecimento: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"Programador Lógico\",\n    \"block.integrateddynamics.coal_generator\": \"Gerador\",\n    \"block.integrateddynamics.coal_generator.info\": \"Produz energia a partir de itens combustíveis\",\n    \"block.integrateddynamics.proxy\": \"Proxy\",\n    \"block.integrateddynamics.proxy.info\": \"Expõe uma referência de variável como um aspecto. Limpe em uma grade de criação.\",\n    \"block.integrateddynamics.materializer\": \"Materializador\",\n    \"block.integrateddynamics.materializer.info\": \"Materializa o valor de uma variável em uma nova variável\",\n    \"block.integrateddynamics.menril_log\": \"Tora de Menril\",\n    \"block.integrateddynamics.menril_log_filled\": \"Tora de Menril Enriquecida\",\n    \"block.integrateddynamics.menril_log_stripped\": \"Tora de Menril Descascada\",\n    \"block.integrateddynamics.menril_leaves\": \"Folhas de Menril\",\n    \"block.integrateddynamics.menril_sapling\": \"Muda de Menril\",\n    \"block.integrateddynamics.menril_planks\": \"Tábuas de Menril\",\n    \"block.integrateddynamics.menril_slab\": \"Laje de Menril\",\n    \"block.integrateddynamics.menril_fence\": \"Cerca de Menril\",\n    \"block.integrateddynamics.menril_fence_gate\": \"Portão de Cerca de Menril\",\n    \"block.integrateddynamics.menril_wood\": \"Madeira de Menril\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"Madeira de Menril Descascada\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Bloco de Menril Cristalizado\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"Tijolo de Menril Cristalizado\",\n    \"block.integrateddynamics.block_menril_resin\": \"Resina de Menril\",\n    \"block.integrateddynamics.drying_basin\": \"Bacia de Secagem\",\n    \"block.integrateddynamics.drying_basin.info\": \"Para secar itens e fluidos\",\n    \"block.integrateddynamics.squeezer\": \"Espremedor\",\n    \"block.integrateddynamics.squeezer.info\": \"Espreme, achata e esmaga ao pular sobre ele. Pulso de Redstone para reiniciar.\",\n    \"block.integrateddynamics.menril_torch\": \"Tocha de Menril\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Tocha de Pedra de Menril\",\n    \"block.integrateddynamics.invisible_light\": \"Luz Invisível\",\n    \"block.integrateddynamics.menril_door\": \"Porta de Menril\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Escadas de Menril\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Escadas de Menril Cristalizado\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"Escadas de Tijolo de Menril Cristalizado\",\n    \"block.integrateddynamics.delay\": \"Atrasador\",\n    \"block.integrateddynamics.delay.info\": \"Expõe o histórico de uma variável como um aspecto. Limpe em uma grade de criação.\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"Chorus Líquido\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"Bloco de Chorus Cristalizado\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"Tijolo de Chorus Cristalizado\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"Escadas de Chorus Cristalizado\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"Escadas de Tijolo de Chorus Cristalizado\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"Espremedor Mecânico\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"Espreme, achata e esmaga usando energia.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"Bacia de Secagem Mecânica\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"Para secar itens e fluidos usando energia.\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"Laje de Menril Cristalizado\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"Laje de Tijolo de Menril Cristalizado\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"Laje de Chorus Cristalizado\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"Laje de Tijolo de Chorus Cristalizado\",\n    \"item.integrateddynamics.general.id\": \"§e§lID: §r%s\",\n    \"item.integrateddynamics.wrench\": \"Chave Inglesa\",\n    \"item.integrateddynamics.wrench.info\": \"Shift + Clique com o botão direito no ar para mudar o modo\",\n    \"item.integrateddynamics.wrench.mode\": \"§eModo: §r%s\",\n    \"item.integrateddynamics.wrench.mode.default\": \"Padrão\",\n    \"item.integrateddynamics.wrench.mode.default.info\": \"Remove peças ou rotaciona blocos\",\n    \"item.integrateddynamics.wrench.mode.offset\": \"Salvar Deslocamento\",\n    \"item.integrateddynamics.wrench.mode.offset.info\": \"Shift + Clique com o botão direito em uma posição e clique com o botão direito na peça para vincular o deslocamento\",\n    \"item.integrateddynamics.wrench.mode.offset.saved\": \"Posição salva na Chave Inglesa: %s\",\n    \"item.integrateddynamics.wrench.mode.offset.success\": \"Nova posição de deslocamento foi definida na peça\",\n    \"item.integrateddynamics.wrench.mode.offset.incomplete\": \"Nenhuma posição foi salva nesta Chave Inglesa ainda\",\n    \"item.integrateddynamics.wrench.mode.offset.fail\": \"Não foi possível definir o deslocamento. Aumente o deslocamento máximo usando Melhorias!\",\n    \"item.integrateddynamics.wrench.mode.offset.pos\": \"Posição: %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side\": \"Salvar Deslocamento e Lado\",\n    \"item.integrateddynamics.wrench.mode.offset_side.info\": \"Shift + Clique com o botão direito em uma posição e clique com o botão direito na peça para vincular o deslocamento e o lado\",\n    \"item.integrateddynamics.wrench.mode.offset_side.saved\": \"Posição e Lado foram salvos na Chave Inglesa: %s - %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side.success\": \"Nova posição de deslocamento e lado foram definidos na peça\",\n    \"item.integrateddynamics.wrench.mode.offset_side.side\": \"Lado: %s\",\n    \"item.integrateddynamics.variable\": \"Cartão de Variável\",\n    \"item.integrateddynamics.variable.info\": \"Limpe ou copie em uma grade de criação\",\n    \"item.integrateddynamics.variable.warning\": \"§4§lAVISO: NÃO copie este item clicando com o botão do meio!\",\n    \"item.integrateddynamics.variable.id\": \"§e§oID da Variável: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"Etiquetador\",\n    \"item.integrateddynamics.labeller.info\": \"Etiqueta suas variáveis. Também funciona no Programador Lógico.\",\n    \"item.integrateddynamics.labeller.button.write\": \"Escrever\",\n    \"item.integrateddynamics.facade\": \"Fachada\",\n    \"item.integrateddynamics.facade.info\": \"Combine com um bloco em uma mesa de trabalho\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"Balde de Chorus Líquido\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"Balde de Resina de Menril\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"Pedaço de Menril Cristalizado\",\n    \"item.integrateddynamics.variable_transformer\": \"Transformadores de Variável de Entrada/Saída\",\n    \"item.integrateddynamics.variable_transformer_output\": \"Transformador de Variável de Saída\",\n    \"item.integrateddynamics.variable_transformer_input\": \"Transformador de Variável de Entrada\",\n    \"item.integrateddynamics.menril_berries\": \"Bagas de Menril\",\n    \"item.integrateddynamics.menril_berries.info\": \"Saborosas e iluminantes\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"Programador Lógico Portátil\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"Sobre a Dinâmica da Integração\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"Um guia de referência completo do mod com tutoriais\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"Pedaço de Chorus Cristalizado\",\n    \"item.integrateddynamics.logic_director\": \"Diretor Lógico\",\n    \"item.integrateddynamics.proto_chorus\": \"Proto Coro\",\n    \"item.integrateddynamics.enhancement_offset\": \"Melhoria de Peça: Deslocamento\",\n    \"item.integrateddynamics.enhancement_offset.info\": \"Aumenta o deslocamento máximo de uma peça ao clicar com o botão direito\",\n    \"item.integrateddynamics.enhancement_offset.tooltip\": \"Aumento de deslocamento: %s\",\n    \"item.integrateddynamics.enhancement_offset.increased\": \"Deslocamento da peça aumentado para %s\",\n    \"item.integrateddynamics.enhancement_offset.limit\": \"O deslocamento da peça já está no limite (%s)\",\n    \"fluid_type.integrateddynamics.menril_resin\": \"Resina de Menril\",\n    \"fluid_type.integrateddynamics.liquid_chorus\": \"Chorus Líquido\",\n    \"biome.integrateddynamics.meneglin\": \"Meneglin\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"Descoberta de Meneglin\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"Corte uma árvore de Menril, que pode ser encontrada mais facilmente nos biomas Meneglin.\",\n    \"advancement.integrateddynamics.squeezing\": \"Espremendo com estilo\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"Crie um Espremedor.\",\n    \"advancement.integrateddynamics.drying\": \"Secando com estilo\",\n    \"advancement.integrateddynamics.drying.desc\": \"Crie uma Bacia de Secagem.\",\n    \"advancement.integrateddynamics.menril_production\": \"Produção de Menril\",\n    \"advancement.integrateddynamics.menril_production.desc\": \"Processe Tora de Menril em Resina de Menril e seque-a para obter Menril Cristalizado.\",\n    \"advancement.integrateddynamics.cables_logic\": \"Cabos, para a lógica\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"Crie um Cabo Lógico\",\n    \"advancement.integrateddynamics.networks_logic\": \"Redes, para a lógica\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"Construa uma rede lógica com pelo menos 10 Cabos Lógicos conectados.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"Chaveando Menril\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"Crie uma Chave Inglesa\",\n    \"advancement.integrateddynamics.variables\": \"Variáveis, Para Valores\",\n    \"advancement.integrateddynamics.variables.desc\": \"Crie um Cartão de Variável\",\n    \"advancement.integrateddynamics.variable_input\": \"Transformação de Entrada de Variável\",\n    \"advancement.integrateddynamics.variable_input.desc\": \"Crie um Transformador de Variável de Entrada\",\n    \"advancement.integrateddynamics.variable_output\": \"Transformação de Saída de Variável\",\n    \"advancement.integrateddynamics.variable_output.desc\": \"Crie um Transformador de Variável de Saída\",\n    \"advancement.integrateddynamics.redstone_reading\": \"Lendo Redstone\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"Crie um Leitor de Redstone\",\n    \"advancement.integrateddynamics.block_reading\": \"Lendo Blocos\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"Crie um Leitor de Bloco\",\n    \"advancement.integrateddynamics.inventory_reading\": \"Lendo Inventários\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"Crie um Leitor de Inventário\",\n    \"advancement.integrateddynamics.machine_reading\": \"Lendo Máquinas\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"Crie um Leitor de Máquina\",\n    \"advancement.integrateddynamics.value_displaying\": \"Exibindo Valores\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"Crie um Painel de Exibição\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"Capturando Redstone\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"Crie um cartão de variável para um valor §6Inteiro§7 de redstone.\",\n    \"advancement.integrateddynamics.redstone_observement\": \"Observação de Redstone\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"Coloque um cartão de variável §6Inteiro§7 em um Painel de Exibição.\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"Transmissão de Redstone\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"Deixe um Escritor de Redstone escrever um nível máximo de redstone inteiro que foi lido por um Leitor de Redstone.\",\n    \"advancement.integrateddynamics.recipe_reading\": \"Leitura de Receita\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"Leia a lista de receitas de uma máquina (como a Mesa de Trabalho) usando um Leitor de Máquina.\",\n    \"advancement.integrateddynamics.recipe_creation\": \"Criação de Receita\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"Crie visualmente uma §8Receita§7 usando o Programador Lógico.\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"Consulta de Receita\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"Consulte dinamicamente a receita de um Baú usando um Leitor de Máquina.\",\n    \"advancement.integrateddynamics.lectern_data\": \"NBT do Púlpito\",\n    \"advancement.integrateddynamics.lectern_data.desc\": \"Leia a tag NBT de uma máquina usando um Leitor de Bloco, como o Púlpito.\",\n    \"advancement.integrateddynamics.lectern_book\": \"Livro NBT do Púlpito\",\n    \"advancement.integrateddynamics.lectern_book.desc\": \"Leia o campo Livro NBT dentro de um bloco Púlpito na tag NBT lida de um Leitor de Bloco.\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"Nome do Livro NBT do Púlpito\",\n    \"advancement.integrateddynamics.lectern_book_name.desc\": \"Leia o nome (id) de um livro dentro de um bloco Púlpito aplicando uma expressão de Caminho NBT na tag NBT lida de um Leitor de Bloco.\",\n    \"advancement.integrateddynamics.logic_programming\": \"Programação Lógica\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"Abra a GUI de um Programador Lógico\",\n    \"advancement.integrateddynamics.constant_definition\": \"Definição de Constante\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"Defina um Cartão de Variável §6Inteiro§7 constante.\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"Adição Aritmética\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"Some dois §eNúmeros§7 e exiba o resultado.\",\n    \"advancement.integrateddynamics.variable_materialization\": \"Materialização de Variável\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"Materialize uma variável em um §lMaterializador§r.\",\n    \"advancement.integrateddynamics.variable_proxying\": \"Proxy de Variável\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"Crie uma variável proxy válida em um §lProxy§r.\",\n    \"advancement.integrateddynamics.logical_list_building\": \"Construção de Lista Lógica\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"Construa uma Lista estática com os §6Inteiros§7 1, 10 e 100.\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"Identificação de Origem do Item\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"Obtenha o nome do mod de um Item.\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"Para O Que Eu Estaria Olhando?\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"Obtenha o Bloco para o qual você está olhando.\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"Adições Dinâmicas\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"Invoque o Operador de adição dinamicamente\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"Filtragem de Lista Dinâmica\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"Filtre uma lista de Inteiros 1, 10 e 100 para 10.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"Domando Creepers\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"Mostre a vida de um Creeper em um Painel de Exibição\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"Som de Passo de Esponja\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"Toque o som de passo de um bloco de Esponja Molhada\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"Recursão Recursiva\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"Crie uma Lista infinita e a exiba em um Painel de Exibição\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"Ferramentas para Obsidian?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"Verifique se uma Picareta de Pedra pode quebrar um bloco de Obsidian e mostre o resultado em um Painel de Exibição.\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"Placa de Pressão Inteligente\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"Crie um sistema que abre automaticamente uma porta quando você passa por um Leitor de Entidade com base na correspondência do nome da entidade, usando um Escritor de Redstone apontando diretamente para a porta. (Dica: passe um nome de entidade como primeiro argumento no operador de igualdade)\",\n    \"valuetype.integrateddynamics.error.value\": \"Valor inválido\",\n    \"valuetype.integrateddynamics.value_type\": \"Tipo de Valor\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§eTipo: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§oValor: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§eAssinatura: §r\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"A entrada \\\"%s\\\" não pôde ser analisada para este tipo de valor.\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"Este tipo de valor requer um item de entrada.\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"Elemento %s é inválido: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"A lista espera o tipo de valor %s, mas %s foi fornecido.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"Um tipo de valor de operador era esperado na posição %s, mas %s foi fornecido\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"O valor do operador %s esperava os tipos de valor (%s), mas (%s) foram fornecidos.\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"O item inserido não é um bloco.\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"O item inserido não contém fluido.\",\n    \"valuetype.integrateddynamics.error.no_light_calculator\": \"O tipo de valor %s não pode ser usado para derivar um nível de luz.\",\n    \"valuetype.integrateddynamics.error.deserialize\": \"Algo deu errado ao desserializar '%s': %s\",\n    \"valuetype.integrateddynamics.any\": \"Qualquer\",\n    \"valuetype.integrateddynamics.any.info\": \"Aceita qualquer tipo de valor.\",\n    \"valuetype.integrateddynamics.number\": \"Número\",\n    \"valuetype.integrateddynamics.number.info\": \"Aceita todos os tipos de número.\",\n    \"valuetype.integrateddynamics.named\": \"Nomeado\",\n    \"valuetype.integrateddynamics.named.info\": \"Algo que tem um nome, como nomes de exibição de itens e blocos.\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"Unicamente Nomeado\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"Algo que tem um nome único, como itens e blocos.\",\n    \"operator.integrateddynamics.string.string_error\": \"Erro\",\n    \"operator.integrateddynamics.string.string_error.info\": \"Lança um erro personalizado\",\n    \"valuetype.integrateddynamics.boolean\": \"Booleano\",\n    \"valuetype.integrateddynamics.integer\": \"Inteiro\",\n    \"valuetype.integrateddynamics.double\": \"Dobro\",\n    \"valuetype.integrateddynamics.long\": \"Longo\",\n    \"valuetype.integrateddynamics.string\": \"String\",\n    \"valuetype.integrateddynamics.operator\": \"Operador\",\n    \"valuetype.integrateddynamics.nbt\": \"NBT\",\n    \"valuetype.integrateddynamics.list\": \"Lista\",\n    \"valuetype.integrateddynamics.block\": \"Bloco\",\n    \"valuetype.integrateddynamics.itemstack\": \"Item\",\n    \"valuetype.integrateddynamics.entity\": \"Entidade\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Fluido\",\n    \"valuetype.integrateddynamics.ingredients\": \"Ingredientes\",\n    \"valuetype.integrateddynamics.recipe\": \"Receita\",\n    \"valuetype.integrateddynamics.ingredients.slot.info\": \"Shift clique para configurar\",\n    \"parttype.integrateddynamics.error.low_energy\": \"Energia insuficiente\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"Sem Energia\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"Inativo\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"Erros:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"Aspecto: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"Valor: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"Grupo: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"AVISO: Nenhum aspecto disponível. Isto pode se tornar utilizável quando outros mods forem instalados.\",\n    \"parttype.integrateddynamics.tooltip.maxoffset\": \"Deslocamento Máx: %s\",\n    \"parttype.integrateddynamics.audio_reader\": \"Leitor de Áudio\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"Lê informações de áudio\",\n    \"parttype.integrateddynamics.block_reader\": \"Leitor de Bloco\",\n    \"parttype.integrateddynamics.block_reader.info\": \"Lê informações de bloco\",\n    \"parttype.integrateddynamics.entity_reader\": \"Leitor de Entidade\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"Lê informações de entidade\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"Leitor Extradimensional\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"Lê informações que são compartilhadas por todas as dimensões\",\n    \"parttype.integrateddynamics.fluid_reader\": \"Leitor de Fluido\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"Lê tanques de fluido\",\n    \"parttype.integrateddynamics.inventory_reader\": \"Leitor de Inventário\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"Lê dados de inventários\",\n    \"parttype.integrateddynamics.network_reader\": \"Leitor de Rede\",\n    \"parttype.integrateddynamics.network_reader.info\": \"Lê propriedades da rede alvo\",\n    \"parttype.integrateddynamics.machine_reader\": \"Leitor de Máquina\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"Lê propriedades da máquina\",\n    \"parttype.integrateddynamics.redstone_reader\": \"Leitor de Redstone\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"Lê valores de redstone\",\n    \"parttype.integrateddynamics.world_reader\": \"Leitor de Mundo\",\n    \"parttype.integrateddynamics.world_reader.info\": \"Lê coisas relacionadas ao mundo, blocos e entidades\",\n    \"parttype.integrateddynamics.audio_writer\": \"Escritor de Áudio\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"Produz áudio e música\",\n    \"parttype.integrateddynamics.effect_writer\": \"Escritor de Efeito\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"Produz partículas\",\n    \"parttype.integrateddynamics.entity_writer\": \"Escritor de Entidade\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"Escreve configuração de entidade\",\n    \"parttype.integrateddynamics.machine_writer\": \"Escritor de Máquina\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"Escreve configuração de máquina\",\n    \"parttype.integrateddynamics.inventory_writer\": \"Escritor de Inventário\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"Escreve em inventários\",\n    \"parttype.integrateddynamics.redstone_writer\": \"Escritor de Redstone\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"Escreve em redstone\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"Este painel não aceita um %s.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"Painel de Luz Estático\",\n    \"parttype.integrateddynamics.static_light_panel.info\": \"Um painel de luz simples que está sempre ativo\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"Painel de Luz Dinâmico\",\n    \"parttype.integrateddynamics.dynamic_light_panel.info\": \"Exibe visualmente o valor de uma variável no mundo como um valor de luz\",\n    \"parttype.integrateddynamics.display_panel\": \"Painel de Exibição\",\n    \"parttype.integrateddynamics.display_panel.info\": \"Exibe visualmente o valor de uma variável no mundo como texto\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"Conector Monodirecional\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"Pode se conectar a outros Conectores Monodirecionais a uma distância em linha reta\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"Conector Onidirecional\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"Pode se conectar a outros Conectores Onidirecionais em qualquer lugar, incluindo dimensões diferentes.\",\n    \"variable.integrateddynamics.error.invalid_item\": \"A variável atual não pode ser usada neste aspecto.\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"A peça com ID %s não foi encontrada na rede atual.\",\n    \"variable.integrateddynamics.error.recursion\": \"A variável com ID %s está se referindo a si mesma.\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§oID do Proxy: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"O Proxy com ID %s não foi encontrada na rede atual.\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"O Proxy com ID %s não está expondo uma variável.\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"O Proxy com ID %s deveria expor uma variável do tipo %s, mas %s foi encontrado.\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§oID do Atraso: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"O Atrasador com ID %s não foi encontrado na rede atual.\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"O Atrasador com ID %s não está expondo uma variável.\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"O Atrasador com ID %s deveria expor uma variável do tipo %s, mas %s foi encontrado.\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§eAspecto: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§eTipo: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§oID da Peça: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"Este aspecto espera %s, mas %s foi fornecido.\",\n    \"aspect.integrateddynamics.error.recursion\": \"A variável de aspecto para %s está se referindo a si mesma.\",\n    \"aspect.integrateddynamics.error.no_value_interface\": \"Nenhuma interface de valor válida foi encontrada.\",\n    \"aspect.integrateddynamics.error.no_value_interface_value\": \"Nenhum valor de interface de valor válido foi encontrado.\",\n    \"aspect.integrateddynamics.name\": \"Aspecto\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp\": \"Nota de Harpa\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp.info\": \"Lê uma nota de harpa, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum\": \"Nota de Bumbo\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum.info\": \"Lê uma nota de bumbo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"Nota de Caixa\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"Lê uma nota de caixa, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat\": \"Nota de Prato\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat.info\": \"Lê uma nota de prato, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass\": \"Nota de Baixo\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass.info\": \"Lê uma nota de baixo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute\": \"Nota de Flauta\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute.info\": \"Lê uma nota de flauta, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell\": \"Nota de Sino\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell.info\": \"Lê uma nota de sino, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar\": \"Nota de Guitarra\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar.info\": \"Lê uma nota de guitarra, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime\": \"Nota de Carrilhão\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime.info\": \"Lê uma nota de carrilhão, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone\": \"Nota de Xilofone\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone.info\": \"Lê uma nota de xilofone, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone\": \"Nota de Xilofone de Ferro\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone.info\": \"Lê uma nota de xilofone de ferro, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell\": \"Nota de Caneca\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell.info\": \"Lê uma nota de caneca, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo\": \"Nota de Didgeridoo\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo.info\": \"Lê uma nota de didgeridoo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit\": \"Nota de Bit\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit.info\": \"Lê uma nota de bit, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo\": \"Nota de Banjo\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo.info\": \"Lê uma nota de banjo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling\": \"Nota de Pling\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling.info\": \"Lê uma nota de pling, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie\": \"Nota de Zumbi\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.zombie.info\": \"Lê uma nota de zumbi, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton\": \"Nota de Esqueleto\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.skeleton.info\": \"Lê uma nota de esqueleto, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper\": \"Nota de Creeper\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.creeper.info\": \"Lê uma nota de creeper, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon\": \"Nota de Dragão\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.dragon.info\": \"Lê uma nota de dragão, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton\": \"Nota de Esqueleto Wither\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.wither_skeleton.info\": \"Lê uma nota de esqueleto wither, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin\": \"Nota de Piglin\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.piglin.info\": \"Lê uma nota de piglin, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head\": \"Nota de Cabeça\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.custom_head.info\": \"Lê uma nota de cabeça, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp\": \"Nota de Harpa\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp.info\": \"Produz uma nota de harpa, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum\": \"Nota de Bumbo\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum.info\": \"Produz uma nota de bumbo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"Nota de Caixa\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"Produz uma nota de caixa, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat\": \"Nota de Prato\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat.info\": \"Produz uma nota de prato, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass\": \"Nota de Baixo\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass.info\": \"Produz uma nota de baixo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute\": \"Nota de Flauta\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute.info\": \"Produz uma nota de flauta, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell\": \"Nota de Sino\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell.info\": \"Produz uma nota de sino, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar\": \"Nota de Guitarra\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar.info\": \"Produz uma nota de guitarra, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime\": \"Nota de Carrilhão\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime.info\": \"Produz uma nota de carrilhão, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone\": \"Nota de Xilofone\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone.info\": \"Produz uma nota de xilofone, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone\": \"Nota de Xilofone de Ferro\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone.info\": \"Produz uma nota de xilofone de ferro, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell\": \"Nota de Caneca\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell.info\": \"Produz uma nota de caneca, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo\": \"Nota de Didgeridoo\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo.info\": \"Produz uma nota de didgeridoo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit\": \"Nota de Bit\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit.info\": \"Produz uma nota de bit, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo\": \"Nota de Banjo\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo.info\": \"Produz uma nota de banjo, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling\": \"Nota de Pling\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling.info\": \"Produz uma nota de pling, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.zombie\": \"Nota de Zumbi\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.zombie.info\": \"Produz uma nota de zumbi, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.skeleton\": \"Nota de Esqueleto\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.skeleton.info\": \"Produz uma nota de esqueleto, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.creeper\": \"Nota de Creeper\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.creeper.info\": \"Produz uma nota de creeper, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.dragon\": \"Nota de Dragão\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.dragon.info\": \"Produz uma nota de dragão, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.wither_skeleton\": \"Nota de Esqueleto Wither\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.wither_skeleton.info\": \"Produz uma nota de esqueleto wither, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.piglin\": \"Nota de Piglin\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.piglin.info\": \"Produz uma nota de piglin, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.custom_head\": \"Nota de Cabeça\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.custom_head.info\": \"Produz uma nota de cabeça, intervalo esperado [0, 24]\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"Som\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"Toca o nome do som fornecido\",\n    \"aspect.integrateddynamics.write.string.audio.text\": \"Texto\",\n    \"aspect.integrateddynamics.write.string.audio.text.info\": \"Converte o texto fornecido em fala\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager\": \"Partícula: Aldeão Zangado\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager.info\": \"Emite uma partícula de aldeão zangado com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble\": \"Partícula: Bolha\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble.info\": \"Emite uma partícula de bolha com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud\": \"Partícula: Nuvem\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud.info\": \"Emite uma partícula de nuvem com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit\": \"Partícula: Crítico\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit.info\": \"Emite uma partícula de crítico com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator\": \"Partícula: Indicador de Dano\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator.info\": \"Emite uma partícula de indicador de dano com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath\": \"Partícula: Hálito de Dragão\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath.info\": \"Emite uma partícula de hálito de dragão com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava\": \"Partícula: Gotejamento de Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava.info\": \"Emite uma partícula de gotejamento de lava com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava\": \"Partícula: Queda de Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava.info\": \"Emite uma partícula de queda de lava com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava\": \"Partícula: Aterrissagem de Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava.info\": \"Emite uma partícula de aterrissagem de lava com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water\": \"Partícula: Gotejamento de Água\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water.info\": \"Emite uma partícula de gotejamento de água com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water\": \"Partícula: Queda de Água\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water.info\": \"Emite uma partícula de queda de água com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect\": \"Partícula: Efeito\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect.info\": \"Emite uma partícula de efeito com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian\": \"Partícula: Guardião Ancião\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian.info\": \"Emite uma partícula de guardião ancião com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit\": \"Partícula: Acerto Encantado\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit.info\": \"Emite uma partícula de acerto encantado com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant\": \"Partícula: Encantada\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant.info\": \"Emite uma partícula encantada com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod\": \"Partícula: Haste do Fim\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod.info\": \"Emite uma partícula de haste do fim com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter\": \"Partícula: Emissor de Explosão\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter.info\": \"Emite uma partícula de emissor de explosão com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion\": \"Partícula: Explosão\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion.info\": \"Emite uma partícula de explosão com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sonic_boom\": \"Partícula: Estrondo Sônico\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sonic_boom.info\": \"Emite uma partícula de estrondo sônico com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework\": \"Partícula: Fogo de Artifício\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework.info\": \"Emite uma partícula de fogo de artifício com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing\": \"Partícula: Pesca\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing.info\": \"Emite uma partícula de pesca com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame\": \"Partícula: Chama\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame.info\": \"Emite uma partícula de chama com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame\": \"Partícula: Chama de Fogo das Almas\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame.info\": \"Emite uma partícula de Chama de Fogo das Almas com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul\": \"Partícula: Alma de Sculk\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul.info\": \"Emite uma partícula de Alma de Sculk com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop\": \"Partícula: Estouro de Carga de Sculk\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop.info\": \"Emite uma partícula de Estouro de Carga de Sculk com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul\": \"Partícula: Alma\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul.info\": \"Emite uma partícula de Alma com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash\": \"Partícula: Flash\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash.info\": \"Emite uma partícula de Flash com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager\": \"Partícula: Aldeão Feliz\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager.info\": \"Emite uma partícula de aldeão feliz com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter\": \"Partícula: Compostor\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter.info\": \"Emite uma partícula de compostor com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart\": \"Partícula: Coração\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart.info\": \"Emite uma partícula de coração com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect\": \"Partícula: Efeito Instantâneo\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect.info\": \"Emite uma partícula de efeito instantâneo com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime\": \"Partícula: Slime\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime.info\": \"Emite uma partícula de slime com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball\": \"Partícula: Bola de Neve\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball.info\": \"Emite uma partícula de bola de neve com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke\": \"Partícula: Fumaça Grande\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke.info\": \"Emite uma partícula de fumaça grande com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava\": \"Partícula: Lava\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava.info\": \"Emite uma partícula de lava com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium\": \"Partícula: Micélio\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium.info\": \"Emite uma partícula de micélio com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note\": \"Partícula: Nota\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note.info\": \"Emite uma partícula de nota com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof\": \"Partícula: Poof\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof.info\": \"Emite uma partícula de poof com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal\": \"Partícula: Portal\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal.info\": \"Emite uma partícula de portal com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain\": \"Partícula: Chuva\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain.info\": \"Emite uma partícula de chuva com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke\": \"Partícula: Fumaça\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke.info\": \"Emite uma partícula de fumaça com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze\": \"Partícula: Espirro\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze.info\": \"Emite uma partícula de espirro com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit\": \"Partícula: Cuspe\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit.info\": \"Emite uma partícula de cuspe com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink\": \"Partícula: Tinta de Lula\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink.info\": \"Emite uma partícula de tinta de lula com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack\": \"Partícula: Ataque de Varredura\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack.info\": \"Emite uma partícula de ataque de varredura com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying\": \"Partícula: Totem da Imortalidade\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying.info\": \"Emite uma partícula de totem da imortalidade com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater\": \"Partícula: Subaquática\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater.info\": \"Emite uma partícula subaquática com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash\": \"Partícula: Splash\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash.info\": \"Emite uma partícula de splash com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch\": \"Partícula: Bruxa\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch.info\": \"Emite uma partícula de bruxa com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop\": \"Partícula: Estouro de Bolha\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop.info\": \"Emite uma partícula de estouro de bolha com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down\": \"Partícula: Corrente Descendente\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down.info\": \"Emite uma partícula de corrente descendente com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up\": \"Partícula: Corrente Ascendente\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up.info\": \"Emite uma partícula de corrente ascendente com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus\": \"Partícula: Nautilus\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus.info\": \"Emite uma partícula de nautilus com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin\": \"Partícula: Golfinho\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin.info\": \"Emite uma partícula de golfinho com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke\": \"Partícula: Fumaça Aconchegante de Fogueira\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke.info\": \"Emite uma partícula de fumaça aconchegante de fogueira com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke\": \"Partícula: Fumaça de Sinal de Fogueira\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke.info\": \"Emite uma partícula de fumaça de sinal de fogueira com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey\": \"Partícula: Gotejamento de Mel\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey.info\": \"Emite uma partícula de gotejamento de mel com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey\": \"Partícula: Queda de Mel\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey.info\": \"Emite uma partícula de queda de mel com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey\": \"Partícula: Aterrissagem de Mel\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey.info\": \"Emite uma partícula de aterrissagem de mel com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar\": \"Partícula: Queda de Néctar\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar.info\": \"Emite uma partícula de queda de néctar com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom\": \"Partícula: Queda de Flor de Esporos\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom.info\": \"Emite uma partícula de queda de flor de esporos com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash\": \"Partícula: Cinza\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash.info\": \"Emite uma partícula de cinza com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore\": \"Partícula: Esporo Carmesim\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore.info\": \"Emite uma partícula de esporo carmesim com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore\": \"Partícula: Esporo Distorcido\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore.info\": \"Emite uma partícula de esporo distorcido com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air\": \"Partícula: Ar de Flor de Esporos\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air.info\": \"Emite uma partícula de ar de flor de esporos com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear\": \"Partícula: Gotejamento de Lágrima de Obsidian\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear.info\": \"Emite uma partícula de gotejamento de lágrima de obsidian com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear\": \"Partícula: Queda de Lágrima de Obsidian\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear.info\": \"Emite uma partícula de queda de lágrima de obsidian com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear\": \"Partícula: Aterrissagem de Lágrima de Obsidian\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear.info\": \"Emite uma partícula de aterrissagem de lágrima de obsidian com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal\": \"Partícula: Portal Reverso\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal.info\": \"Emite uma partícula de portal reverso com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash\": \"Partícula: Cinza Branca\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash.info\": \"Emite uma partícula de cinza branca com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame\": \"Partícula: Chama Pequena\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame.info\": \"Emite uma partícula de chama pequena com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake\": \"Partícula: Floco de Neve\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake.info\": \"Emite uma partícula de floco de neve com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava\": \"Partícula: Gotejamento de Lava de Dripstone\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava.info\": \"Emite uma partícula de gotejamento de lava de dripstone com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava\": \"Partícula: Queda de Lava de Dripstone\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava.info\": \"Emite uma partícula de queda de lava de dripstone com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water\": \"Partícula: Gotejamento de Água de Dripstone\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water.info\": \"Emite uma partícula de gotejamento de água de dripstone com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water\": \"Partícula: Queda de Água de Dripstone\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water.info\": \"Emite uma partícula de queda de água de dripstone com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink\": \"Partícula: Tinta de Lula Brilhante\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink.info\": \"Emite uma partícula de tinta de lula brilhante com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow\": \"Partícula: Brilho\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow.info\": \"Emite uma partícula de brilho com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on\": \"Partícula: Encerar\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on.info\": \"Emite uma partícula de encerar com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off\": \"Partícula: Remover Cera\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off.info\": \"Emite uma partícula de remover cera com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark\": \"Partícula: Faísca Elétrica\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark.info\": \"Emite uma partícula de faísca elétrica com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape\": \"Partícula: Raspagem\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape.info\": \"Emite uma partícula de raspagem com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_egg_crack\": \"Partícula: Rachadura de Ovo\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_egg_crack.info\": \"Emite uma partícula de rachadura de ovo com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dust_plume\": \"Partícula: Pluma de Poeira\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dust_plume.info\": \"Emite uma partícula de pluma de poeira com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player\": \"Partícula: Gerador de Teste Detectou Jogador\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player.info\": \"Emite uma partícula de gerador de teste detectou jogador com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player_ominous\": \"Partícula: Gerador de Teste Detectou Jogador Sinistro\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_spawner_detected_player_ominous.info\": \"Emite uma partícula de gerador de teste detectou jogador sinistro com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_vault_connection\": \"Partícula: Conexão de Cofre\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_vault_connection.info\": \"Emite uma partícula de conexão de cofre com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ominous_spawning\": \"Partícula: Geração Sinistra\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ominous_spawning.info\": \"Emite uma partícula de geração sinistra com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_raid_omen\": \"Partícula: Presságio de Invasão\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_raid_omen.info\": \"Emite uma partícula de presságio de invasão com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_omen\": \"Partícula: Presságio de Teste\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_trial_omen.info\": \"Emite uma partícula de presságio de teste com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firefly\": \"Partícula: Vaga-lume\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firefly.info\": \"Emite uma partícula de vaga-lume com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_pause_mob_growth\": \"Partícula: Pausar Crescimento de Criatura\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_pause_mob_growth.info\": \"Emite uma partícula de pausar crescimento de criatura com uma certa velocidade.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reset_mob_growth\": \"Partícula: Resetar Crescimento de Criatura\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reset_mob_growth.info\": \"Emite uma partícula de resetar crescimento de criatura com uma certa velocidade.\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"Tem Bloco\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"Se o espaço alvo tem um bloco\",\n    \"aspect.integrateddynamics.read.string.block.dimension\": \"Dimensão\",\n    \"aspect.integrateddynamics.read.string.block.dimension.info\": \"O id da dimensão do mundo alvo\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"Coordenada X\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"A coordenada X do alvo\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Coordenada Y\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"A coordenada Y do alvo\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Coordenada Z\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"A coordenada Z do alvo\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"NBT da Tile Entity\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"A Tile Entity alvo como NBT\",\n    \"aspect.integrateddynamics.read.string.block.biome\": \"Bioma\",\n    \"aspect.integrateddynamics.read.string.block.biome.info\": \"O nome do bioma no alvo\",\n    \"aspect.integrateddynamics.read.integer.block.light\": \"Nível de Luz\",\n    \"aspect.integrateddynamics.read.integer.block.light.info\": \"O nível de luz no alvo\",\n    \"aspect.integrateddynamics.read.block.block\": \"Bloco\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"O bloco alvo\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"Rotação da Moldura\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"O índice da rotação que o item na Moldura anexada possui.\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"Entidades\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"As entidades no espaço alvo\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"Jogadores\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"Os jogadores que estão no espaço alvo\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"Entidade\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"A entidade selecionada no espaço alvo\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"Moldura\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"O item em uma Moldura anexada ao leitor\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"Aleatório\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"Um valor inteiro aleatório\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"Contagem de Jogadores\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"A quantidade de jogadores no servidor\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"Tempo de Tick\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"O tempo médio de tick em nanossegundos para o servidor\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"O número médio de ticks por segundo em todos os mundos\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"Jogadores\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"Os jogadores que estão online neste servidor\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"Tanque Vazio\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"Se o tanque de fluido alvo está vazio\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"Tanque Cheio\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"Se o tanque de fluido alvo está cheio\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"Tanque Não Vazio\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"Se o tanque de fluido alvo tem algo dentro\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"É Tanque\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"Se o alvo tem um tanque de fluido\",\n    \"aspect.integrateddynamics.read.long.fluid.amount\": \"Quantidade de Fluido\",\n    \"aspect.integrateddynamics.read.long.fluid.amount.info\": \"A quantidade de fluido em mB no tanque ativo\",\n    \"aspect.integrateddynamics.read.long.fluid.totalamount\": \"Quantidade Total de Fluido\",\n    \"aspect.integrateddynamics.read.long.fluid.totalamount.info\": \"A quantidade total de fluido em mB\",\n    \"aspect.integrateddynamics.read.long.fluid.capacity\": \"Capacidade de Fluido\",\n    \"aspect.integrateddynamics.read.long.fluid.capacity.info\": \"Capacidade total do tanque ativo\",\n    \"aspect.integrateddynamics.read.long.fluid.totalcapacity\": \"Capacidade Total de Fluido\",\n    \"aspect.integrateddynamics.read.long.fluid.totalcapacity.info\": \"Capacidade total do tanque\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"Tanques\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"A quantidade de tanques encontrados no alvo\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"Taxa de Preenchimento de Fluido\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"A quantidade de fluido no tanque ativo dividida por sua capacidade\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"Fluidos do Tanque\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"A lista de fluidos em todos os tanques\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"Capacidades do Tanque\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"A lista de capacidades de todos os tanques\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"Fluido do Tanque\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"O fluido no tanque alvo\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"Fluido do Bloco\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"O fluido no bloco alvo\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"Inventário Cheio\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"Se todos os slots tiverem pelo menos um item\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"Inventário Vazio\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"Se todos os slots estiverem vazios\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"Inventário Não Vazio\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"Se houver pelo menos um item\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"É Inventário\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"Se o alvo tem um inventário\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"Contagem do Inventário\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"Obtém a quantidade total de itens\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"Tamanho da Pilha do Slot\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"Obtém o tamanho da pilha no slot ativo\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"Slots\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"O número de slots no inventário\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"Slots Preenchidos\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"O número de slots que têm um item\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"Taxa de Preenchimento\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"O número de slots não vazios dividido pelo número total de slots\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"Itens\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"Obtém uma lista dos itens no inventário\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"Item do Slot\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"Obtém o Item no slot ativo\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"É Trabalhador\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"Se o alvo é uma máquina trabalhadora\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"Tem Trabalho\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"Se o alvo tem trabalho a processar\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"Pode Trabalhar\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"Se o alvo seria capaz de começar a trabalhar em seu estado atual\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"Está Trabalhando\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"Se o alvo está trabalhando atualmente\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"Tem Temperatura\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"Se o alvo tem uma certa temperatura\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"Temperatura\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature.info\": \"A temperatura atual do alvo em Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"Temperatura Máxima\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature.info\": \"A temperatura máxima do alvo em Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"Temperatura Mínima\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature.info\": \"A temperatura mínima do alvo em Kelvin\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"Temperatura Padrão\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature.info\": \"A temperatura padrão do alvo em Kelvin\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"É Manipulador de Receitas\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"Se o alvo pode manipular receitas\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"Receitas\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"Receitas que podem ser manipuladas pelo alvo\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"Saída de Receita por Entrada\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"Um operador que retorna a saída da receita resultante para uma dada entrada.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"Entradas de Receita por Saída\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"Um operador que retorna todas as entradas de receita possíveis para uma dada saída.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"Receitas por Entrada\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"Um operador que retorna todas as receitas possíveis para uma dada entrada.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"Receitas por Saída\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"Um operador que retorna todas as receitas possíveis para uma dada saída.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"Receita por Entrada\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"Um operador que retorna uma receita para uma dada entrada.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"Receita por Saída\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"Um operador que retorna uma receita para uma dada saída.\",\n    \"aspect.integrateddynamics.read.boolean.energy.applicable\": \"É Manipulador de Energia\",\n    \"aspect.integrateddynamics.read.boolean.energy.applicable.info\": \"Se o alvo de alguma forma manipula energia\",\n    \"aspect.integrateddynamics.read.boolean.energy.canextract\": \"Pode Extrair Energia\",\n    \"aspect.integrateddynamics.read.boolean.energy.canextract.info\": \"Se a energia pode realmente ser extraída do alvo, leva em conta o armazenamento\",\n    \"aspect.integrateddynamics.read.boolean.energy.caninsert\": \"Pode Inserir Energia\",\n    \"aspect.integrateddynamics.read.boolean.energy.caninsert.info\": \"Se a energia pode realmente ser inserida no alvo, leva em conta o armazenamento e a capacidade\",\n    \"aspect.integrateddynamics.read.boolean.energy.isfull\": \"O Buffer de Energia está Cheio\",\n    \"aspect.integrateddynamics.read.boolean.energy.isfull.info\": \"Se o buffer de energia do alvo está completamente cheio\",\n    \"aspect.integrateddynamics.read.boolean.energy.isempty\": \"O Buffer de Energia está Vazio\",\n    \"aspect.integrateddynamics.read.boolean.energy.isempty.info\": \"Se o buffer de energia do alvo está completamente vazio\",\n    \"aspect.integrateddynamics.read.boolean.energy.isnonempty\": \"O Buffer de Energia não está Vazio\",\n    \"aspect.integrateddynamics.read.boolean.energy.isnonempty.info\": \"Se o buffer de energia do alvo não está vazio\",\n    \"aspect.integrateddynamics.read.long.energy.amount\": \"Energia Armazenada\",\n    \"aspect.integrateddynamics.read.long.energy.amount.info\": \"A quantidade de energia armazenada no alvo\",\n    \"aspect.integrateddynamics.read.long.energy.capacity\": \"Capacidade de Energia\",\n    \"aspect.integrateddynamics.read.long.energy.capacity.info\": \"A capacidade de energia do alvo\",\n    \"aspect.integrateddynamics.read.double.energy.fillratio\": \"Taxa de Preenchimento de Energia\",\n    \"aspect.integrateddynamics.read.double.energy.fillratio.info\": \"A quantidade de energia no alvo dividida por sua capacidade\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"É Rede\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"Se o alvo tem uma rede\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"Elementos\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"A quantidade de elementos na rede alvo\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"Baterias de Energia\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"A quantidade de baterias de energia na rede alvo\",\n    \"aspect.integrateddynamics.read.long.network.energy.stored\": \"Energia Armazenada\",\n    \"aspect.integrateddynamics.read.long.network.energy.stored.info\": \"A quantidade de energia disponível na rede alvo\",\n    \"aspect.integrateddynamics.read.long.network.energy.max\": \"Capacidade de Energia\",\n    \"aspect.integrateddynamics.read.long.network.energy.max.info\": \"A capacidade de energia da rede\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"Taxa de Consumo de Energia\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"A taxa de consumo de energia atual de todas as peças do ID na rede alvo por tick\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"Valor\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"O valor que está sendo exposto pelo alvo\",\n    \"aspect.integrateddynamics.read.operator.network.variablebyid\": \"Valor da Variável por ID\",\n    \"aspect.integrateddynamics.read.operator.network.variablebyid.info\": \"Um operador que retorna o valor de uma variável por seu identificador inteiro\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"Redstone Alto\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"Se o nível de redstone está no máximo\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"Redstone Baixo\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"Se não há nível de redstone\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"Redstone\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"Se há um nível de redstone\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"Clock de Redstone\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"Pulsa com um comprimento e intervalo configuráveis.\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"Redstone\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"Obtém o nível exato de redstone\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"Comparador\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"Obtém o valor do comparador\",\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"Redstone\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"Produz um sinal de redstone completo se for verdadeiro\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"Pulso de Redstone\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"Produz um pulso com o valor de redstone configurado se o valor dado mudar.\",\n    \"aspect.integrateddynamics.write.integer.redstone\": \"Redstone\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"Produz o valor como sinal de redstone exato\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"Pulso de Redstone\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"Produz um pulso com o valor de redstone configurado se o valor dado mudar.\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"Tem Entidade\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"Se o espaço alvo tem uma entidade\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"Tem Mob\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"Se o espaço alvo tem um mob\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"Tem Animal\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"Se o espaço alvo tem um animal\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"Tem Jogador\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"Se o espaço alvo tem um jogador\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"Tem Item\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"Se o espaço alvo tem um item\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"Clima: Limpo\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"Se o clima está limpo no mundo\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"Clima: Chuva\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining.info\": \"Se está chovendo no mundo\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"Clima: Trovão\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"Se há uma tempestade de raios no mundo\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"É Dia\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"Se é dia no mundo alvo\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"É Noite\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"Se é noite no mundo alvo\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"Entidades\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"A quantidade de entidades no espaço alvo\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"Contagem Regressiva da Chuva\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"A quantidade de ticks até que chova neste mundo\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"Tempo de Tick\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"O tempo médio de tick em milissegundos para este mundo\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"Hora do Dia\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"O tempo que passou no dia do mundo alvo\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"Nível de Luz\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"O nível de luz no alvo\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"Contagem de Jogadores\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"Os jogadores que estão online neste mundo\",\n    \"aspect.integrateddynamics.read.double.world.tps\": \"TPS\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"O número de ticks por segundo neste mundo\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"Tempo\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"O tempo do mundo dependente do comando set time\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"Tempo Total\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"O tempo do mundo independente do comando set time\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"Nome do Mundo\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"O nome do mundo alvo\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"Jogadores\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"Os jogadores que estão neste mundo\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"ID do Tanque\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"ID do Slot\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"Elemento da Lista\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"Alcance\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"Canal\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"Intervalo do Pulso\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"Duração do Pulso\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"Deslocamento de Tempo do Pulso\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"Volume\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"Frequência\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"Deslocamento X\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Deslocamento Y\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Deslocamento Z\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"Contagem de Partículas\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"Dispersão X\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Dispersão Y\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Dispersão Z\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"Carga Forte\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"Se uma Tocha de Redstone deve ser simulada em vez de Pó de Redstone normal\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"Valor do Pulso\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"O valor do pulso que deve ser emitido.\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_length\": \"Duração do Pulso\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_length.info\": \"A duração do pulso em ticks.\",\n    \"operator.integrateddynamics\": \"Operador\",\n    \"operator.integrateddynamics.applied.operator_name\": \"%s Aplicado [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"§Aplicado a: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§eOperador: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§eCategoria: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§eIDs da Variável: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§eTipo de Entrada %s: §r\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§eTipo de Saída: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"O operador %s recebeu uma entrada de comprimento %s enquanto precisa de um comprimento de %s.\",\n    \"operator.integrateddynamics.error.operator_param_wrong_input_length\": \"Um comprimento de entrada de operador de %s é necessário, enquanto %s tem apenas um comprimento de entrada de %s.\",\n    \"operator.integrateddynamics.error.null_type\": \"O operador %s recebeu uma entrada com uma variável nula na posição %s.\",\n    \"operator.integrateddynamics.error.wrong_type\": \"O operador %s recebeu uma entrada com o tipo %s na posição %s enquanto o tipo %s era esperado.\",\n    \"operator.integrateddynamics.error.wrong_type_output\": \"A avaliação para o operador %s retornou %s em vez do esperado %s.\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"O operador %s recebeu uma entrada com o tipo %s na posição %s enquanto o tipo %s era esperado para currying.\",\n    \"operator.integrateddynamics.error.currying_overflow\": \"Ao fazer currying do operador %s (%s entradas) com %s entradas, um operador era esperado como saída, mas obteve-se %s.\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"O operador %s recebeu um operador de entrada que produz o tipo %s enquanto o tipo %s era esperado.\",\n    \"operator.integrateddynamics.error.illegal_property\": \"A propriedade deve produzir %s, enquanto %s foi encontrado para o operador %s.\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"A variável com ID %s não foi encontrada na rede atual.\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"A variável com ID %s depende de si mesma, recursão infinita.\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"O operador %s recebeu o operador %s com comprimento de entrada %s enquanto o comprimento %s é necessário.\",\n    \"operator.integrateddynamics.error.operator_recursion_limit\": \"Atingido o limite de recursão do operador de %s invocações para %s. Tentando travar o jogo, você está?\",\n    \"operator.integrateddynamics.error.operator_nbt_path_expression\": \"Encontrada uma expressão de caminho NBT inválida '%s': %s\",\n    \"operator.integrateddynamics.error.cast.no_mapping\": \"Não existe mapeamento de conversão de %s para %s.\",\n    \"operator.integrateddynamics.error.cast.illegal\": \"Tentativa de converter %s para %s, para o valor \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.cast.unexpected\": \"O valor do tipo %s não corresponde ao tipo esperado %s para converter para %s.\",\n    \"operator.integrateddynamics.error.divide_by_zero\": \"Divisão por zero não é permitida.\",\n    \"operator.integrateddynamics.error.parse\": \"O valor \\\"%s\\\" não pôde ser analisado para um %s.\",\n    \"operator.integrateddynamics.error.no_derserializer\": \"Nenhum serializador foi encontrado para desserializar o valor do operador \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.regex.invalid\": \"Regex inválido: \\\"%s\\\"\",\n    \"operator.integrateddynamics.error.substring.to_greater_than_from\": \"A posição final da substring deve ser estritamente maior que a posição inicial.\",\n    \"operator.integrateddynamics.error.substring.index_negative\": \"Os índices da substring não devem ser negativos.\",\n    \"operator.integrateddynamics.error.substring.longer_than_string\": \"Os índices da substring não devem exceder o comprimento da string.\",\n    \"operator.integrateddynamics.error.group.index_negative\": \"O índice do grupo não deve ser negativo.\",\n    \"operator.integrateddynamics.error.group.no_match\": \"Nenhuma correspondência de regex de grupo foi encontrada para o regex \\\"%s\\\" em \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.group.no_match_group\": \"Nenhuma correspondência de regex de grupo foi encontrada para o regex \\\"%s\\\" em \\\"%s\\\" para o grupo \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.regex_scan.index_negative\": \"O índice de varredura do regex não deve ser negativo.\",\n    \"operator.integrateddynamics.error.regex_scan.no_match_group\": \"Nenhuma correspondência de varredura regex foi encontrada para a regex \\\"%s\\\" em \\\"%s\\\" para o grupo \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.replace_regex.invalid_group\": \"Grupo inválido na substituição regex para a substituição \\\"%s\\\": %s\",\n    \"operator.integrateddynamics.error.infinite_list.illegal\": \"O operador %s não pode ser aplicado em listas infinitas.\",\n    \"operator.integrateddynamics.error.index_out_of_bounds\": \"Índice fora dos limites. Tentativa de obter o elemento %s de uma lista de comprimento %s. Use o operador getOrDefault para fornecer um padrão quando um índice estiver fora dos limites.\",\n    \"operator.integrateddynamics.error.slice.to_greater_than_from\": \"A posição final da fatia deve ser estritamente maior que a posição inicial.\",\n    \"operator.integrateddynamics.error.slice.index_negative\": \"Os índices da fatia não devem ser negativos.\",\n    \"operator.integrateddynamics.error.reduce.empty\": \"O operador reduce1 tentou obter o primeiro elemento de uma lista vazia. Use o operador reduce para fornecer um valor base para suportar listas vazias.\",\n    \"operator.integrateddynamics.error.operator_not_found\": \"Não foi possível encontrar o operador com o nome \\\"%s\\\".\",\n    \"operator.integrateddynamics.logical\": \"Lógico\",\n    \"operator.integrateddynamics.logical.basename\": \"Lógico %s\",\n    \"operator.integrateddynamics.logical.and\": \"E\",\n    \"operator.integrateddynamics.logical.or\": \"Ou\",\n    \"operator.integrateddynamics.logical.not\": \"Não\",\n    \"operator.integrateddynamics.logical.nand\": \"Nand\",\n    \"operator.integrateddynamics.logical.nor\": \"Nor\",\n    \"operator.integrateddynamics.arithmetic\": \"Aritmético\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"Aritmético %s\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"Adição\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"Subtração\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"Multiplicação\",\n    \"operator.integrateddynamics.arithmetic.division\": \"Divisão\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"Máximo\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"Pega o maior de dois valores.\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"Mínimo\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"Pega o menor de dois valores.\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"Módulo\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"Incremento\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"Decremento\",\n    \"operator.integrateddynamics.integer\": \"Inteiro\",\n    \"operator.integrateddynamics.integer.basename\": \"Inteiro %s\",\n    \"operator.integrateddynamics.integer.modulus\": \"Módulo\",\n    \"operator.integrateddynamics.integer.increment\": \"Incremento\",\n    \"operator.integrateddynamics.integer.decrement\": \"Decremento\",\n    \"operator.integrateddynamics.relational\": \"Relacional\",\n    \"operator.integrateddynamics.relational.basename\": \"Relacional %s\",\n    \"operator.integrateddynamics.relational.equals\": \"Igual a\",\n    \"operator.integrateddynamics.relational.equals.info\": \"Testa se dois valores são iguais. Os tipos de ambos devem ser os mesmos.\",\n    \"operator.integrateddynamics.relational.gt\": \"Maior que\",\n    \"operator.integrateddynamics.relational.lt\": \"Menor que\",\n    \"operator.integrateddynamics.relational.notequals\": \"Diferente de\",\n    \"operator.integrateddynamics.relational.notequals.info\": \"Testa se dois valores são diferentes. Os tipos de ambos devem ser os mesmos.\",\n    \"operator.integrateddynamics.relational.ge\": \"Maior ou Igual a\",\n    \"operator.integrateddynamics.relational.le\": \"Menor ou Igual a\",\n    \"operator.integrateddynamics.binary\": \"Binário\",\n    \"operator.integrateddynamics.binary.basename\": \"Binário %s\",\n    \"operator.integrateddynamics.binary.and\": \"E\",\n    \"operator.integrateddynamics.binary.or\": \"Ou\",\n    \"operator.integrateddynamics.binary.xor\": \"Xor\",\n    \"operator.integrateddynamics.binary.complement\": \"Complemento\",\n    \"operator.integrateddynamics.binary.rshift\": \"Deslocamento à Direita\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"Deslocamento à direita com preenchimento de uns\",\n    \"operator.integrateddynamics.binary.lshift\": \"Deslocamento à Esquerda\",\n    \"operator.integrateddynamics.binary.rzshift\": \"Deslocamento à Direita com Zero\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"Deslocamento à direita com preenchimento de zeros\",\n    \"operator.integrateddynamics.string\": \"String\",\n    \"operator.integrateddynamics.string.basename\": \"String %s\",\n    \"operator.integrateddynamics.string.length\": \"Comprimento\",\n    \"operator.integrateddynamics.string.length.info\": \"O comprimento da String fornecida\",\n    \"operator.integrateddynamics.string.concat\": \"Concatenar\",\n    \"operator.integrateddynamics.string.concat.info\": \"Concatena as duas Strings fornecidas em uma só\",\n    \"operator.integrateddynamics.string.contains\": \"String Contém\",\n    \"operator.integrateddynamics.string.contains.info\": \"Se a substring fornecida está contida na string principal.\",\n    \"operator.integrateddynamics.string.contains_regex\": \"Contém Regex\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"Se a expressão regular fornecida corresponde a qualquer substring da string principal.\",\n    \"operator.integrateddynamics.string.matches_regex\": \"Corresponde a Regex\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"Se a expressão regular fornecida corresponde à string principal.\",\n    \"operator.integrateddynamics.string.index_of\": \"Índice De\",\n    \"operator.integrateddynamics.string.index_of.info\": \"Obtém o índice da primeira ocorrência de uma substring que corresponde ao termo de busca na string principal.\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"Índice da Regex\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"Obtém o índice da primeira ocorrência de uma substring que corresponde ao padrão na string principal.\",\n    \"operator.integrateddynamics.string.starts_with\": \"Começa Com\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"Se a substring fornecida corresponde ao início da string principal.\",\n    \"operator.integrateddynamics.string.ends_with\": \"Termina Com\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"Se a string fornecida corresponde ao final da string principal.\",\n    \"operator.integrateddynamics.string.split_on\": \"Dividir Em\",\n    \"operator.integrateddynamics.string.split_on.info\": \"Obtém uma lista contendo pedaços da string principal, dividida pelo delimitador (literal) fornecido.\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"Dividir por Regex\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"Obtém uma lista contendo pedaços da string principal, dividida pela expressão regular fornecida.\",\n    \"operator.integrateddynamics.string.substring\": \"Substring\",\n    \"operator.integrateddynamics.string.substring.info\": \"Obtém uma substring da string principal, do índice inicial (inclusivo) ao índice final (exclusivo).\",\n    \"operator.integrateddynamics.string.regex_group\": \"Grupo Regex\",\n    \"operator.integrateddynamics.string.regex_group.info\": \"Procura pela expressão regular fornecida e retorna, nessa correspondência, o grupo no índice especificado, na string principal.\",\n    \"operator.integrateddynamics.string.regex_groups\": \"Grupos Regex\",\n    \"operator.integrateddynamics.string.regex_groups.info\": \"Procura pela expressão regular fornecida e retorna, nessa correspondência, uma lista de todos os seus grupos, na string principal.\",\n    \"operator.integrateddynamics.string.regex_scan\": \"Varredura Regex\",\n    \"operator.integrateddynamics.string.regex_scan.info\": \"Procura por todas as correspondências da expressão regular fornecida e retorna o grupo no índice especificado, na string principal.\",\n    \"operator.integrateddynamics.string.replace\": \"Substituir\",\n    \"operator.integrateddynamics.string.replace.info\": \"Encontra todas as instâncias do termo de busca e as substitui pela string fornecida, na string principal.\",\n    \"operator.integrateddynamics.string.replace_regex\": \"Substituir Regex\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"Encontra todas as correspondências do padrão de busca e as substitui pela string fornecida, na string principal.\",\n    \"operator.integrateddynamics.string.join\": \"Juntar\",\n    \"operator.integrateddynamics.string.join.info\": \"Cria uma string unida com o delimitador de string fornecido e a lista de strings fornecida.\",\n    \"operator.integrateddynamics.string.name\": \"Para String\",\n    \"operator.integrateddynamics.string.name.info\": \"Obtém a string ou nome de um objeto nomeado ou o converte para uma string\",\n    \"operator.integrateddynamics.string.unique_name\": \"Nome Único\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"Obtém o nome único de um objeto\",\n    \"operator.integrateddynamics.double\": \"Dobro\",\n    \"operator.integrateddynamics.double.basename\": \"Dobro %s\",\n    \"operator.integrateddynamics.double.sqrt\": \"Raiz Quadrada\",\n    \"operator.integrateddynamics.double.pow\": \"Potência\",\n    \"operator.integrateddynamics.number\": \"Número\",\n    \"operator.integrateddynamics.number.basename\": \"Número %s\",\n    \"operator.integrateddynamics.number.round\": \"Arredondar\",\n    \"operator.integrateddynamics.number.round.info\": \"Arredonda para o Inteiro mais próximo\",\n    \"operator.integrateddynamics.number.ceil\": \"Teto\",\n    \"operator.integrateddynamics.number.ceil.info\": \"Arredonda para cima para o Inteiro mais próximo\",\n    \"operator.integrateddynamics.number.floor\": \"Piso\",\n    \"operator.integrateddynamics.number.floor.info\": \"Arredonda para baixo para o Inteiro mais próximo\",\n    \"operator.integrateddynamics.number.compact\": \"Compactar\",\n    \"operator.integrateddynamics.number.compact.info\": \"Mostra uma representação mais curta e menos precisa de um Número\",\n    \"operator.integrateddynamics.number.cast_double\": \"Converter Número para Dupla\",\n    \"operator.integrateddynamics.number.cast_double.info\": \"Converte qualquer Número em uma Dupla\",\n    \"operator.integrateddynamics.number.cast_long\": \"Converter Número para Longo\",\n    \"operator.integrateddynamics.number.cast_long.info\": \"Converte qualquer Número em um Longo\",\n    \"operator.integrateddynamics.number.cast_integer\": \"Converter Número para Inteiro\",\n    \"operator.integrateddynamics.number.cast_integer.info\": \"Converte qualquer Número em um Inteiro\",\n    \"operator.integrateddynamics.list\": \"Lista\",\n    \"operator.integrateddynamics.list.basename\": \"Lista %s\",\n    \"operator.integrateddynamics.list.length\": \"Comprimento\",\n    \"operator.integrateddynamics.list.length.info\": \"O comprimento da lista fornecida\",\n    \"operator.integrateddynamics.list.empty\": \"Vazia\",\n    \"operator.integrateddynamics.list.empty.info\": \"Se a lista está vazia\",\n    \"operator.integrateddynamics.list.notempty\": \"Não Vazia\",\n    \"operator.integrateddynamics.list.notempty.info\": \"Se a lista não está vazia\",\n    \"operator.integrateddynamics.list.get\": \"Obter\",\n    \"operator.integrateddynamics.list.get.info\": \"Obtém o elemento da lista na posição fornecida, lança um erro se o índice estiver fora dos limites.\",\n    \"operator.integrateddynamics.list.get_or_default\": \"Obter ou Padrão\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"Obtém com segurança o elemento da lista na posição fornecida; se esse elemento não estiver disponível, retorna o valor padrão fornecido.\",\n    \"operator.integrateddynamics.list.contains\": \"Contém\",\n    \"operator.integrateddynamics.list.contains.info\": \"Se a lista contém o elemento fornecido.\",\n    \"operator.integrateddynamics.list.contains_p\": \"Contém Predicado\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"Se pelo menos um dos elementos da lista retorna verdadeiro para o predicado fornecido.\",\n    \"operator.integrateddynamics.list.count\": \"Contar\",\n    \"operator.integrateddynamics.list.count.info\": \"O número de vezes que o elemento fornecido é encontrado na lista.\",\n    \"operator.integrateddynamics.list.count_p\": \"Contar Predicado\",\n    \"operator.integrateddynamics.list.count_p.info\": \"O número de vezes que o predicado fornecido retorna verdadeiro para os elementos na lista.\",\n    \"operator.integrateddynamics.list.append\": \"Anexar\",\n    \"operator.integrateddynamics.list.append.info\": \"Anexa o item fornecido à lista fornecida.\",\n    \"operator.integrateddynamics.list.concat\": \"Concatenar\",\n    \"operator.integrateddynamics.list.concat.info\": \"Concatena as duas listas uma na outra.\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"Construtor de Lista Preguiçoso\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"Constrói uma lista preguiçosamente usando um valor inicial e um operador que é aplicado ao elemento anterior para obter o próximo elemento.\",\n    \"operator.integrateddynamics.list.head\": \"Cabeça\",\n    \"operator.integrateddynamics.list.head.info\": \"Obtém o primeiro elemento da lista fornecida.\",\n    \"operator.integrateddynamics.list.tail\": \"Cauda\",\n    \"operator.integrateddynamics.list.tail.info\": \"Remove o primeiro elemento da lista.\",\n    \"operator.integrateddynamics.list.uniq_p\": \"Predicado Único\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"Remove duplicatas de todos os elementos na lista com base no predicado comparador fornecido.\",\n    \"operator.integrateddynamics.list.uniq\": \"Único\",\n    \"operator.integrateddynamics.list.uniq.info\": \"Remove duplicatas de todos os elementos na lista.\",\n    \"operator.integrateddynamics.list.slice\": \"Fatia\",\n    \"operator.integrateddynamics.list.slice.info\": \"Pega um subconjunto da lista fornecida do índice inicial (inclusivo) ao índice final (exclusivo).\",\n    \"operator.integrateddynamics.list.intersection\": \"Interseção\",\n    \"operator.integrateddynamics.list.intersection.info\": \"Retorna uma lista de itens presentes em ambas as listas fornecidas\",\n    \"operator.integrateddynamics.list.equals_set\": \"Igualdade de Lista (Conjunto)\",\n    \"operator.integrateddynamics.list.equals_set.info\": \"Verifica se duas listas são iguais de acordo com a semântica de conjunto.\",\n    \"operator.integrateddynamics.list.equals_multiset\": \"Igualdade de Lista (Multiconjunto)\",\n    \"operator.integrateddynamics.list.equals_multiset.info\": \"Verifica se duas listas são iguais de acordo com a semântica de multiconjunto.\",\n    \"operator.integrateddynamics.block\": \"Bloco\",\n    \"operator.integrateddynamics.block.basename\": \"Bloco %s\",\n    \"operator.integrateddynamics.block.opaque\": \"Opaco\",\n    \"operator.integrateddynamics.block.opaque.info\": \"Se o bloco fornecido é opaco, não transparente\",\n    \"operator.integrateddynamics.block.itemstack\": \"Item\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"A representação em item do bloco fornecido\",\n    \"operator.integrateddynamics.block.mod\": \"Mod\",\n    \"operator.integrateddynamics.block.mod.info\": \"O nome do mod proprietário do bloco fornecido\",\n    \"operator.integrateddynamics.block.breaksound\": \"Som de Quebra do Bloco\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"O som de quebra do bloco fornecido\",\n    \"operator.integrateddynamics.block.placesound\": \"Som de Colocação do Bloco\",\n    \"operator.integrateddynamics.block.placesound.info\": \"O som de colocação do bloco fornecido\",\n    \"operator.integrateddynamics.block.stepsound\": \"Som de Passo do Bloco\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"O som de passo do bloco fornecido\",\n    \"operator.integrateddynamics.block.isshearable\": \"Bloco é Tosquiável\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"Se o bloco fornecido é tosquiável\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"Bloco Pode Sustentar Planta em Bloco\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"Se o bloco fornecido pode sustentar a planta em bloco fornecida\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"Bloco Pode Sustentar Planta em Item\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"Se o bloco fornecido pode sustentar a planta em item fornecida\",\n    \"operator.integrateddynamics.block.plantage\": \"Idade da Planta do Bloco\",\n    \"operator.integrateddynamics.block.plantage.info\": \"A idade do bloco de planta fornecido\",\n    \"operator.integrateddynamics.block.blockbyname\": \"Bloco por Nome\",\n    \"operator.integrateddynamics.block.blockbyname.info\": \"Obtenha o bloco correspondente ao nome único dado.\",\n    \"operator.integrateddynamics.block.blockproperties\": \"Propriedades do Bloco\",\n    \"operator.integrateddynamics.block.blockproperties.info\": \"Obtém as propriedades do bloco como uma tag NBT composta.\",\n    \"operator.integrateddynamics.block.blockfromproperties\": \"Bloco a Partir de Propriedades\",\n    \"operator.integrateddynamics.block.blockfromproperties.info\": \"Obtém o bloco fornecido com as propriedades fornecidas aplicadas.\",\n    \"operator.integrateddynamics.block.blockpossibleproperties\": \"Propriedades Possíveis do Bloco\",\n    \"operator.integrateddynamics.block.blockpossibleproperties.info\": \"Obtém todas as propriedades possíveis do bloco como uma tag NBT composta com valores de lista.\",\n    \"operator.integrateddynamics.block.tag\": \"Nomes de Tag de Bloco\",\n    \"operator.integrateddynamics.block.tag.info\": \"Os nomes de Tag (strings) do bloco fornecido\",\n    \"operator.integrateddynamics.string.blocktag\": \"Valores de Tag de Bloco\",\n    \"operator.integrateddynamics.string.blocktag.info\": \"Os valores de Tag (blocos) do nome de tag de bloco fornecido\",\n    \"operator.integrateddynamics.itemstack\": \"Item\",\n    \"operator.integrateddynamics.itemstack.basename\": \"Item %s\",\n    \"operator.integrateddynamics.itemstack.size\": \"Tamanho\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"O tamanho atual da pilha\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"Tamanho Máx.\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"O tamanho máximo da pilha\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"Empilhável\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"Se a pilha de itens pode conter pelo menos dois itens.\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"Pode Sofrer Dano\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"Se o item pode sofrer dano.\",\n    \"operator.integrateddynamics.itemstack.damage\": \"Dano\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"O dano atual do item\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"Dano Máx.\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"O dano máximo do item\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"Encantado\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"Se o item está encantado\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"Encantável\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"Se o item pode ser encantado\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"Custo de Reparo\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"O custo de reparo do item\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"Raridade\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"A raridade do item\",\n    \"operator.integrateddynamics.itemstack.strength\": \"Força vs Bloco\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"A força que este item tem contra o bloco fornecido\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"Pode Coletar\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"Se o item pode coletar o bloco fornecido\",\n    \"operator.integrateddynamics.itemstack.block\": \"Bloco\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"O bloco do item fornecido\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"Possui Fluido\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"Se o item fornecido possui um fluido\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"Fluido\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"O fluido do item fornecido\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"Capacidade de Fluido\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"A capacidade de fluido do item fornecido em mB\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"NBT igual\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"Se os componentes de dados dos itens fornecidos são iguais\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt\": \"Item bruto igual sem NBT\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt.info\": \"Se os itens brutos das pilhas fornecidas são iguais, ignorando componentes de dados e contagem de itens, mas incluindo valores de dano.\",\n    \"operator.integrateddynamics.itemstack.israwitemequal\": \"Item bruto igual\",\n    \"operator.integrateddynamics.itemstack.israwitemequal.info\": \"Se os itens brutos dos itens fornecidos são iguais, ignorando NBT, contagem de itens e valores de dano.\",\n    \"operator.integrateddynamics.itemstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"O nome do mod proprietário do item fornecido\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"Tempo de Queima de Combustível\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"O tempo de queima de combustível em ticks do item fornecido\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"É Combustível\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"Se o item fornecido pode ser usado como combustível\",\n    \"operator.integrateddynamics.itemstack.tag\": \"Nomes de Tag de Item\",\n    \"operator.integrateddynamics.itemstack.tag.info\": \"Os nomes de Tag (strings) do item fornecido\",\n    \"operator.integrateddynamics.string.tag\": \"Valores de Tag de Item\",\n    \"operator.integrateddynamics.string.tag.info\": \"Os valores de Tag (itens) do nome de tag de item fornecido\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"Item Possui Inventário\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"Se o item fornecido possui um inventário\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"Tamanho do Inventário do Item\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"Recupera o tamanho do inventário do conteúdo do manipulador de itens fornecido\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"Inventário do Item\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"Recupera o inventário do conteúdo do manipulador de itens fornecido\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"Item com Tamanho da Pilha\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"Define o tamanho da pilha para o item fornecido\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"Item por Nome\",\n    \"operator.integrateddynamics.itemstack.itembyname.info\": \"Obtenha o item correspondente ao nome único.\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"Contagem de Itens na Lista\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"Obtém a contagem total de itens exatamente iguais ao item fornecido em uma lista.\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"NBT do Item\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"Obtém os dados do item fornecido como NBT.\",\n    \"operator.integrateddynamics.itemstack.hasnbt\": \"Possui NBT\",\n    \"operator.integrateddynamics.itemstack.hasnbt.info\": \"Se a pilha de itens possui componentes de dados.\",\n    \"operator.integrateddynamics.itemstack.datakeys\": \"Chaves de Dados\",\n    \"operator.integrateddynamics.itemstack.datakeys.info\": \"Obtém todas as chaves de componentes de dados da pilha de itens.\",\n    \"operator.integrateddynamics.itemstack.datavalue\": \"Valor do Dado\",\n    \"operator.integrateddynamics.itemstack.datavalue.info\": \"Obtém o valor da chave de componente de dados fornecida.\",\n    \"operator.integrateddynamics.itemstack.withdata\": \"Com Dados\",\n    \"operator.integrateddynamics.itemstack.withdata.info\": \"Adiciona a chave e o valor do componente de dados fornecidos à pilha de itens fornecida.\",\n    \"operator.integrateddynamics.entity\": \"Entidade\",\n    \"operator.integrateddynamics.entity.basename\": \"Entidade %s\",\n    \"operator.integrateddynamics.entity.ismob\": \"É Mob\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"Se a entidade é um mob\",\n    \"operator.integrateddynamics.entity.isanimal\": \"É Animal\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"Se a entidade é um animal\",\n    \"operator.integrateddynamics.entity.isitem\": \"É Item\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"Se a entidade é um item\",\n    \"operator.integrateddynamics.entity.isplayer\": \"É Jogador\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"Se a entidade é um jogador\",\n    \"operator.integrateddynamics.entity.isminecart\": \"É Carrinho de Minas\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"Se a entidade é um carrinho de minas\",\n    \"operator.integrateddynamics.entity.item\": \"Item\",\n    \"operator.integrateddynamics.entity.item.info\": \"O item da entidade-item\",\n    \"operator.integrateddynamics.entity.health\": \"Vida\",\n    \"operator.integrateddynamics.entity.health.info\": \"A vida da entidade\",\n    \"operator.integrateddynamics.entity.width\": \"Largura\",\n    \"operator.integrateddynamics.entity.width.info\": \"A largura da entidade\",\n    \"operator.integrateddynamics.entity.height\": \"Altura\",\n    \"operator.integrateddynamics.entity.height.info\": \"A altura da entidade\",\n    \"operator.integrateddynamics.entity.isburning\": \"Está Queimando\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"Se a entidade está queimando\",\n    \"operator.integrateddynamics.entity.iswet\": \"Está Molhada\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"Se a entidade está molhada\",\n    \"operator.integrateddynamics.entity.iscrouching\": \"Está Agachada\",\n    \"operator.integrateddynamics.entity.iscrouching.info\": \"Se a entidade está agachada\",\n    \"operator.integrateddynamics.entity.iseating\": \"Está Comendo\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"Se a entidade está comendo\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"Inventário de Armadura\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"A lista de itens que a entidade está vestindo como armadura.\",\n    \"operator.integrateddynamics.entity.inventory\": \"Inventário do Jogador\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"A lista de itens que o jogador está carregando.\",\n    \"operator.integrateddynamics.entity.mod\": \"Mod\",\n    \"operator.integrateddynamics.entity.mod.info\": \"O nome do mod proprietário da entidade fornecida\",\n    \"operator.integrateddynamics.entity.targetblock\": \"Bloco Alvo\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"O bloco que a entidade fornecida está olhando no momento.\",\n    \"operator.integrateddynamics.entity.targetentity\": \"Entidade Alvo\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"A entidade que a entidade fornecida está olhando no momento.\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"Tem Gui Aberta\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"Se o jogador fornecido tem uma GUI externa aberta.\",\n    \"operator.integrateddynamics.entity.helditem\": \"Item Segurado na Mão Principal\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"O item que a entidade fornecida está segurando em sua mão principal.\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"Item Segurado na Mão Secundária\",\n    \"operator.integrateddynamics.entity.helditemoffhand.info\": \"O item que a entidade fornecida está segurando em sua mão secundária.\",\n    \"operator.integrateddynamics.entity.mounted\": \"Entidades Montadas\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"As entidades que estão atualmente montadas na entidade fornecida.\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"Conteúdo da Moldura\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"O conteúdo da Moldura de Item fornecida.\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"Rotação da Moldura\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"A rotação da Moldura de Item fornecida.\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"Som de Dano da Entidade\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"O som de dano da entidade fornecida.\",\n    \"operator.integrateddynamics.entity.deathsound\": \"Som de Morte da Entidade\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"O som de morte da entidade fornecida.\",\n    \"operator.integrateddynamics.entity.age\": \"Idade da Entidade\",\n    \"operator.integrateddynamics.entity.age.info\": \"A idade da entidade fornecida.\",\n    \"operator.integrateddynamics.entity.ischild\": \"Entidade é Filhote\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"Se a entidade fornecida é um filhote.\",\n    \"operator.integrateddynamics.entity.canbreed\": \"Entidade Pode Acasalar\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"Se a entidade fornecida está pronta para ser acasalada.\",\n    \"operator.integrateddynamics.entity.isinlove\": \"Entidade Está Apaixonada\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"Se a entidade fornecida está apaixonada e pronta para acasalar.\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"Entidade Pode Acasalar Com\",\n    \"operator.integrateddynamics.entity.canbreedwith.info\": \"Se a entidade fornecida pode ser acasalada usando o item fornecido.\",\n    \"operator.integrateddynamics.entity.isshearable\": \"Entidade é Tosquiável\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"Se a entidade fornecida é tosquiável\",\n    \"operator.integrateddynamics.entity.nbt\": \"NBT da Entidade\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"Obtém a entidade fornecida como NBT.\",\n    \"operator.integrateddynamics.entity.entitytype\": \"Tipo de Entidade\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"O nome do tipo da entidade.\",\n    \"operator.integrateddynamics.entity.entityitems\": \"Itens da Entidade\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"Os itens que estão contidos nesta entidade.\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"Fluidos da Entidade\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"Os fluidos que estão contidos nesta entidade.\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"Energia Armazenada na Entidade\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"A quantidade de energia que está armazenada nesta entidade.\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"Capacidade de Energia da Entidade\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"A capacidade de energia desta entidade.\",\n    \"operator.integrateddynamics.entity.entityitemtooltip\": \"Dica de Ferramenta da Entidade\",\n    \"operator.integrateddynamics.entity.entityitemtooltip.info\": \"No contexto da Entidade Jogador fornecida, obtém a dica de ferramenta do item fornecido como uma lista de strings.\",\n    \"operator.integrateddynamics.itemstack.tooltip\": \"Dica de Ferramenta\",\n    \"operator.integrateddynamics.itemstack.tooltip.info\": \"Obtém a dica de ferramenta do item fornecido como uma lista de strings.\",\n    \"operator.integrateddynamics.itemstack.isenergycontainer\": \"É Recipiente de Energia\",\n    \"operator.integrateddynamics.itemstack.isenergycontainer.info\": \"Se o item fornecido pode conter energia\",\n    \"operator.integrateddynamics.itemstack.storedenergy\": \"Energia Armazenada\",\n    \"operator.integrateddynamics.itemstack.storedenergy.info\": \"A quantidade de energia armazenada neste item\",\n    \"operator.integrateddynamics.itemstack.energycapacity\": \"Capacidade de Energia\",\n    \"operator.integrateddynamics.itemstack.energycapacity.info\": \"A quantidade máxima de energia que pode ser armazenada neste item\",\n    \"operator.integrateddynamics.fluidstack\": \"Fluido\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"Fluido %s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"Quantidade\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"A quantidade de mB\",\n    \"operator.integrateddynamics.fluidstack.block\": \"Bloco\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"O bloco do fluido\",\n    \"operator.integrateddynamics.fluidstack.light_level\": \"Nível de Luz\",\n    \"operator.integrateddynamics.fluidstack.light_level.info\": \"O nível de luz emitido pelo fluido\",\n    \"operator.integrateddynamics.fluidstack.density\": \"Densidade\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"A densidade do fluido\",\n    \"operator.integrateddynamics.fluidstack.temperature\": \"Temperatura\",\n    \"operator.integrateddynamics.fluidstack.temperature.info\": \"A temperatura do fluido\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"Viscosidade\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"A viscosidade do fluido\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air\": \"É Mais Leve que o Ar\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air.info\": \"Se o fluido é mais leve que o ar\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"Raridade\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"A raridade do fluido\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize\": \"Som de vaporização do fluido\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize.info\": \"O som de vaporização do fluido\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty\": \"Som de esvaziar o balde\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty.info\": \"O som de esvaziar o balde para o fluido\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill\": \"Som de encher o balde\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill.info\": \"O som de encher o balde para o fluido\",\n    \"operator.integrateddynamics.fluidstack.bucket\": \"Balde\",\n    \"operator.integrateddynamics.fluidstack.bucket.info\": \"O balde cheio para o fluido\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal\": \"Fluido bruto igual\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal.info\": \"Se os fluidos brutos são iguais, ignorando a quantidade\",\n    \"operator.integrateddynamics.fluidstack.mod\": \"Mod\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"O nome do mod proprietário do fluido fornecido\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"NBT do Fluido\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"Obtém os dados do fluido fornecido como NBT.\",\n    \"operator.integrateddynamics.fluidstack.with_amount\": \"Fluido com Quantidade\",\n    \"operator.integrateddynamics.fluidstack.with_amount.info\": \"Copia o fluido fornecido com a quantidade fornecida\",\n    \"operator.integrateddynamics.fluidstack.datakeys\": \"Chaves de Dados\",\n    \"operator.integrateddynamics.fluidstack.datakeys.info\": \"Obtém todas as chaves de componentes de dados da pilha de fluido.\",\n    \"operator.integrateddynamics.fluidstack.datavalue\": \"Valor do Dado\",\n    \"operator.integrateddynamics.fluidstack.datavalue.info\": \"Obtém o valor da chave de componente de dados fornecida.\",\n    \"operator.integrateddynamics.fluidstack.withdata\": \"Com Dados\",\n    \"operator.integrateddynamics.fluidstack.withdata.info\": \"Adiciona a chave e o valor do componente de dados fornecidos à pilha de fluido fornecida.\",\n    \"operator.integrateddynamics.fluidstack.tag\": \"Nomes de Tag de Fluido\",\n    \"operator.integrateddynamics.fluidstack.tag.info\": \"Os nomes de Tag (strings) do fluido fornecido\",\n    \"operator.integrateddynamics.string.fluidtag\": \"Valores de Tag de Fluido\",\n    \"operator.integrateddynamics.string.fluidtag.info\": \"Os valores de Tag (fluidos) do nome de tag de fluido fornecido\",\n    \"operator.integrateddynamics.fluidstack.fluidbyname\": \"Fluído Por Nome\",\n    \"operator.integrateddynamics.fluidstack.fluidbyname.info\": \"Obtenha o fluido correspondente ao nome único.\",\n    \"operator.integrateddynamics.nbt\": \"NBT\",\n    \"operator.integrateddynamics.nbt.basename\": \"NBT %s\",\n    \"operator.integrateddynamics.nbt.compound_size\": \"Tamanho do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_size.info\": \"O número de entradas dentro da tag NBT composta fornecida\",\n    \"operator.integrateddynamics.nbt.compound_keys\": \"Chaves do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_keys.info\": \"A lista de chaves dentro da tag NBT composta fornecida\",\n    \"operator.integrateddynamics.nbt.compound_haskey\": \"Composto NBT Possui Chave\",\n    \"operator.integrateddynamics.nbt.compound_haskey.info\": \"Se a tag NBT composta fornecida contém a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_type\": \"Tipo de Entrada do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_type.info\": \"O tipo de valor na tag NBT composta fornecida correspondente à chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_tag\": \"Valor do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_tag.info\": \"O valor de qualquer tipo na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean\": \"Valor Booleano do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean.info\": \"O valor Booleano na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_integer\": \"Valor Inteiro do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_integer.info\": \"O valor Inteiro na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_long\": \"Valor Long do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_long.info\": \"O valor Long na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_double\": \"Valor Double do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_double.info\": \"O valor Double na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_string\": \"Valor String do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_string.info\": \"O valor String na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_compound\": \"Valor Composto do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_compound.info\": \"O valor Composto na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag\": \"Valor Lista NBT do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag.info\": \"O valor Lista NBT na tag NBT composta fornecida com a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte\": \"Valor Array de Bytes do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte.info\": \"O Array de Bytes na tag NBT composta fornecida com a chave fornecida como Lista de Inteiros\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int\": \"Valor Array de Inteiros do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int.info\": \"O Array de Inteiros na tag NBT composta fornecida com a chave fornecida como Lista de Inteiros\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long\": \"Valor Array de Longs do Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long.info\": \"O Array de Longs na tag NBT composta fornecida com a chave fornecida como Lista de Longs\",\n    \"operator.integrateddynamics.nbt.compound_without\": \"Composto NBT Sem\",\n    \"operator.integrateddynamics.nbt.compound_without.info\": \"Obtém uma cópia da tag NBT composta fornecida sem a chave fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean\": \"Composto NBT com Booleano\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada Booleana fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_short\": \"Composto NBT com Short\",\n    \"operator.integrateddynamics.nbt.compound_with_short.info\": \"Obtém uma cópia da tag NBT composta fornecida com o Inteiro fornecido como uma entrada short\",\n    \"operator.integrateddynamics.nbt.compound_with_integer\": \"Composto NBT com Inteiro\",\n    \"operator.integrateddynamics.nbt.compound_with_integer.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada Inteira fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_long\": \"Composto NBT com Long\",\n    \"operator.integrateddynamics.nbt.compound_with_long.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada Long fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_double\": \"Composto NBT com Double\",\n    \"operator.integrateddynamics.nbt.compound_with_double.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada Double fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_float\": \"Composto NBT com Float\",\n    \"operator.integrateddynamics.nbt.compound_with_float.info\": \"Obtém uma cópia da tag NBT composta fornecida com o Double fornecido como uma entrada float\",\n    \"operator.integrateddynamics.nbt.compound_with_string\": \"Composto NBT com String\",\n    \"operator.integrateddynamics.nbt.compound_with_string.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada String fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_tag\": \"Composto NBT com NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_tag.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada NBT fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag\": \"Composto NBT com Lista NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada de Lista NBT fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte\": \"Composto NBT com Lista de Bytes\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte.info\": \"Obtém uma cópia da tag NBT composta fornecida com a Lista de Inteiros fornecida como uma entrada de Array de Bytes NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int\": \"Composto NBT com Lista de Int\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada de Array de Inteiros NBT fornecida\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long\": \"Composto NBT com Lista de Long\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long.info\": \"Obtém uma cópia da tag NBT composta fornecida com a entrada de Array de Longs NBT fornecida\",\n    \"operator.integrateddynamics.nbt.compound_subset\": \"Subconjunto de Composto NBT\",\n    \"operator.integrateddynamics.nbt.compound_subset.info\": \"Se a primeira tag NBT composta é um subconjunto de, ou igual à segunda tag NBT composta. Isso verificará recursivamente as tags aninhadas.\",\n    \"operator.integrateddynamics.nbt.compound_union\": \"União de Compostos NBT\",\n    \"operator.integrateddynamics.nbt.compound_union.info\": \"A união das tags NBT compostas fornecidas. As tags aninhadas serão unidas recursivamente.\",\n    \"operator.integrateddynamics.nbt.compound_intersection\": \"Interseção de Compostos NBT\",\n    \"operator.integrateddynamics.nbt.compound_intersection.info\": \"A interseção das tags NBT compostas fornecidas. As tags aninhadas serão intersectadas recursivamente.\",\n    \"operator.integrateddynamics.nbt.compound_minus\": \"Subtração de Compostos NBT\",\n    \"operator.integrateddynamics.nbt.compound_minus.info\": \"A diferença das tags NBT compostas fornecidas. As tags aninhadas serão subtraídas recursivamente.\",\n    \"operator.integrateddynamics.nbt.as_boolean\": \"NBT Booleano como Booleano\",\n    \"operator.integrateddynamics.nbt.as_boolean.info\": \"Obtém o valor Booleano da tag NBT Byte fornecida\",\n    \"operator.integrateddynamics.nbt.as_byte\": \"NBT Byte como Inteiro\",\n    \"operator.integrateddynamics.nbt.as_byte.info\": \"Obtém o valor Inteiro da tag NBT Byte fornecida\",\n    \"operator.integrateddynamics.nbt.as_short\": \"NBT Short como Inteiro\",\n    \"operator.integrateddynamics.nbt.as_short.info\": \"Obtém o valor Inteiro da tag NBT Short fornecida\",\n    \"operator.integrateddynamics.nbt.as_int\": \"NBT Inteiro como Inteiro\",\n    \"operator.integrateddynamics.nbt.as_int.info\": \"Obtém o valor Inteiro da tag NBT Inteiro fornecida\",\n    \"operator.integrateddynamics.nbt.as_long\": \"NBT Long como Long\",\n    \"operator.integrateddynamics.nbt.as_long.info\": \"Obtém o valor Long da tag NBT Long fornecida\",\n    \"operator.integrateddynamics.nbt.as_double\": \"NBT Double como Double\",\n    \"operator.integrateddynamics.nbt.as_double.info\": \"Obtém o valor Double da tag NBT Double fornecida\",\n    \"operator.integrateddynamics.nbt.as_float\": \"NBT Float como Double\",\n    \"operator.integrateddynamics.nbt.as_float.info\": \"Obtém o valor Double da tag NBT Float fornecida\",\n    \"operator.integrateddynamics.nbt.as_string\": \"NBT String como String\",\n    \"operator.integrateddynamics.nbt.as_string.info\": \"Obtém o valor String da tag NBT String fornecida\",\n    \"operator.integrateddynamics.nbt.as_tag_list\": \"Lista NBT como Lista NBT\",\n    \"operator.integrateddynamics.nbt.as_tag_list.info\": \"Obtém o valor de Lista NBT da tag NBT Lista fornecida\",\n    \"operator.integrateddynamics.nbt.as_byte_list\": \"Array de Bytes NBT como Lista de Bytes\",\n    \"operator.integrateddynamics.nbt.as_byte_list.info\": \"Obtém o valor de Lista de Bytes da tag NBT Array de Bytes fornecida\",\n    \"operator.integrateddynamics.nbt.as_int_list\": \"Array de Inteiros NBT como Lista de Inteiros\",\n    \"operator.integrateddynamics.nbt.as_int_list.info\": \"Obtém o valor de Lista de Inteiros da tag NBT Array de Inteiros fornecida\",\n    \"operator.integrateddynamics.nbt.as_long_list\": \"Array de Longs NBT como Lista de Longs\",\n    \"operator.integrateddynamics.nbt.as_long_list.info\": \"Obtém o valor de Lista de Longs da tag NBT Array de Longs fornecida\",\n    \"operator.integrateddynamics.nbt.from_boolean\": \"NBT Byte de Booleano\",\n    \"operator.integrateddynamics.nbt.from_boolean.info\": \"Cria uma tag NBT Byte a partir do valor Booleano fornecido\",\n    \"operator.integrateddynamics.nbt.from_short\": \"NBT Short de Inteiro\",\n    \"operator.integrateddynamics.nbt.from_short.info\": \"Cria uma tag NBT Short a partir do valor Inteiro fornecido\",\n    \"operator.integrateddynamics.nbt.from_byte\": \"NBT Byte de Inteiro\",\n    \"operator.integrateddynamics.nbt.from_byte.info\": \"Cria uma tag NBT Byte a partir do valor Inteiro fornecido\",\n    \"operator.integrateddynamics.nbt.from_int\": \"NBT Inteiro de Inteiro\",\n    \"operator.integrateddynamics.nbt.from_int.info\": \"Cria uma tag NBT Inteiro a partir do valor Inteiro fornecido\",\n    \"operator.integrateddynamics.nbt.from_long\": \"NBT Long de Long\",\n    \"operator.integrateddynamics.nbt.from_long.info\": \"Cria uma tag NBT Long a partir do valor Long fornecido\",\n    \"operator.integrateddynamics.nbt.from_double\": \"NBT Double de Double\",\n    \"operator.integrateddynamics.nbt.from_double.info\": \"Cria uma tag NBT Double a partir do valor Double fornecido\",\n    \"operator.integrateddynamics.nbt.from_float\": \"NBT Float de Double\",\n    \"operator.integrateddynamics.nbt.from_float.info\": \"Cria uma tag NBT Float a partir do valor Double fornecido\",\n    \"operator.integrateddynamics.nbt.from_string\": \"NBT String de String\",\n    \"operator.integrateddynamics.nbt.from_string.info\": \"Cria uma tag NBT String a partir do valor String fornecido\",\n    \"operator.integrateddynamics.nbt.from_tag_list\": \"Lista NBT de Lista de Tags\",\n    \"operator.integrateddynamics.nbt.from_tag_list.info\": \"Cria uma tag NBT Lista a partir do valor de Lista NBT fornecido\",\n    \"operator.integrateddynamics.nbt.from_byte_list\": \"Array de Bytes NBT de Lista de Bytes\",\n    \"operator.integrateddynamics.nbt.from_byte_list.info\": \"Cria uma tag NBT Array de Bytes a partir do valor de Lista de Inteiros fornecido\",\n    \"operator.integrateddynamics.nbt.from_int_list\": \"Array de Inteiros NBT de Lista de Inteiros\",\n    \"operator.integrateddynamics.nbt.from_int_list.info\": \"Cria uma tag NBT Array de Inteiros a partir do valor de Lista de Inteiros fornecido\",\n    \"operator.integrateddynamics.nbt.from_long_list\": \"Array de Longs NBT de Lista de Longs\",\n    \"operator.integrateddynamics.nbt.from_long_list.info\": \"Cria uma tag NBT Array de Longs a partir do valor de Lista de Longs fornecido\",\n    \"operator.integrateddynamics.nbt.path_match_first\": \"Primeira Correspondência do Caminho NBT\",\n    \"operator.integrateddynamics.nbt.path_match_first.info\": \"Aplica a expressão de Caminho NBT fornecida no valor NBT fornecido e retorna a primeira correspondência\",\n    \"operator.integrateddynamics.nbt.path_match_all\": \"Todas as Correspondências do Caminho NBT\",\n    \"operator.integrateddynamics.nbt.path_match_all.info\": \"Aplica a expressão de Caminho NBT fornecida no valor NBT fornecido e retorna todas as correspondências como uma lista\",\n    \"operator.integrateddynamics.nbt.path_test\": \"Teste de Caminho NBT\",\n    \"operator.integrateddynamics.nbt.path_test.info\": \"Testa se a expressão de Caminho NBT fornecida corresponde ao valor NBT fornecido\",\n    \"operator.integrateddynamics.operator\": \"Operador\",\n    \"operator.integrateddynamics.operator.basename\": \"Operador %s\",\n    \"operator.integrateddynamics.operator.apply\": \"Aplicar\",\n    \"operator.integrateddynamics.operator.apply.info\": \"Aplica um valor fornecido a um operador fornecido.\",\n    \"operator.integrateddynamics.operator.apply2\": \"Aplicar 2\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"Aplica os dois valores fornecidos a um operador fornecido.\",\n    \"operator.integrateddynamics.operator.apply3\": \"Aplicar 3\",\n    \"operator.integrateddynamics.operator.apply3.info\": \"Aplica os três valores fornecidos a um operador fornecido.\",\n    \"operator.integrateddynamics.operator.apply_n\": \"Aplicar N\",\n    \"operator.integrateddynamics.operator.apply_n.info\": \"Aplica a lista de valores fornecida a um operador fornecido.\",\n    \"operator.integrateddynamics.operator.apply0\": \"Aplicar 0\",\n    \"operator.integrateddynamics.operator.apply0.info\": \"Aplica um operador fornecido sem argumentos.\",\n    \"operator.integrateddynamics.operator.map\": \"Mapear\",\n    \"operator.integrateddynamics.operator.map.info\": \"Aplica o operador fornecido a todos os elementos de uma lista, resultando em uma nova lista de valores mapeados.\",\n    \"operator.integrateddynamics.operator.filter\": \"Filtrar\",\n    \"operator.integrateddynamics.operator.filter.info\": \"Filtra uma lista de elementos combinando todos eles com o predicado fornecido.\",\n    \"operator.integrateddynamics.operator.conjunction\": \"Conjunção\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"Pega a conjunção de dois predicados.\",\n    \"operator.integrateddynamics.operator.disjunction\": \"Disjunção\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"Pega a disjunção de dois predicados.\",\n    \"operator.integrateddynamics.operator.negation\": \"Negação\",\n    \"operator.integrateddynamics.operator.negation.info\": \"Pega a negação de um predicado.\",\n    \"operator.integrateddynamics.operator.pipe\": \"Tubo\",\n    \"operator.integrateddynamics.operator.pipe.info\": \"Cria um novo operador que direciona a saída do primeiro operador para o segundo operador.\",\n    \"operator.integrateddynamics.operator.pipe2\": \"Tubo 2\",\n    \"operator.integrateddynamics.operator.pipe2.info\": \"Cria um novo operador que fornece sua entrada para o primeiro e segundo operadores, e direciona as saídas de ambos para o terceiro operador.\",\n    \"operator.integrateddynamics.operator.flip\": \"Inverter\",\n    \"operator.integrateddynamics.operator.flip.info\": \"Inverte os dois primeiros parâmetros de entrada de um operador.\",\n    \"operator.integrateddynamics.virtual.flipped\": \"Invertido\",\n    \"operator.integrateddynamics.operator.reduce\": \"Reduzir\",\n    \"operator.integrateddynamics.operator.reduce.info\": \"Aplica o operador fornecido em todos os elementos de uma lista para reduzir a lista a um único valor.\",\n    \"operator.integrateddynamics.operator.reduce1\": \"Reduzir 1\",\n    \"operator.integrateddynamics.operator.reduce1.info\": \"Aplica o operador fornecido em todos os elementos de uma lista para reduzir a lista a um único valor. reduce1(op, lista) é equivalente a reduce(op, tail(lista), head(lista)).\",\n    \"operator.integrateddynamics.operator.by_name\": \"Operador por Nome\",\n    \"operator.integrateddynamics.operator.by_name.info\": \"Obtém o operador que tem o nome único fornecido.\",\n    \"operator.integrateddynamics.virtual.recipeoutputbyinput\": \"Saída da Receita por Entrada\",\n    \"operator.integrateddynamics.virtual.recipeinputsbyoutput\": \"Entradas da Receita por Saída\",\n    \"operator.integrateddynamics.virtual.recipesbyinput\": \"Receitas por Entrada\",\n    \"operator.integrateddynamics.virtual.recipesbyoutput\": \"Receitas por Saída\",\n    \"operator.integrateddynamics.ingredients\": \"Ingredientes\",\n    \"operator.integrateddynamics.ingredients.basename\": \"Ingredientes %s\",\n    \"operator.integrateddynamics.ingredients.items\": \"Itens do ingrediente\",\n    \"operator.integrateddynamics.ingredients.items.info\": \"A lista de itens\",\n    \"operator.integrateddynamics.ingredients.fluids\": \"Fluidos do ingrediente\",\n    \"operator.integrateddynamics.ingredients.fluids.info\": \"A lista de fluidos\",\n    \"operator.integrateddynamics.ingredients.energies\": \"Elementos de energia do ingrediente\",\n    \"operator.integrateddynamics.ingredients.energies.info\": \"A lista de elementos de energia\",\n    \"operator.integrateddynamics.ingredients.with_item\": \"Ingredientes com Item\",\n    \"operator.integrateddynamics.ingredients.with_item.info\": \"Obtém uma cópia dos ingredientes fornecidos com o item fornecido na posição de ingrediente fornecida\",\n    \"operator.integrateddynamics.ingredients.with_fluid\": \"Ingredientes com Fluido\",\n    \"operator.integrateddynamics.ingredients.with_fluid.info\": \"Obtém uma cópia dos ingredientes fornecidos com o fluido fornecido na posição de ingrediente fornecida\",\n    \"operator.integrateddynamics.ingredients.with_energy\": \"Ingredientes com Energia\",\n    \"operator.integrateddynamics.ingredients.with_energy.info\": \"Obtém uma cópia dos ingredientes fornecidos com a energia fornecida na posição de ingrediente fornecida\",\n    \"operator.integrateddynamics.ingredients.with_items\": \"Ingredientes com Itens\",\n    \"operator.integrateddynamics.ingredients.with_items.info\": \"Obtém uma cópia dos ingredientes fornecidos com a lista de itens fornecida na posição de ingrediente fornecida\",\n    \"operator.integrateddynamics.ingredients.with_fluids\": \"Ingredientes com Fluidos\",\n    \"operator.integrateddynamics.ingredients.with_fluids.info\": \"Obtém uma cópia dos ingredientes fornecidos com a lista de fluidos fornecida na posição de ingrediente fornecida\",\n    \"operator.integrateddynamics.ingredients.with_energies\": \"Ingredientes com Elementos de Energia\",\n    \"operator.integrateddynamics.ingredients.with_energies.info\": \"Obtém uma cópia dos ingredientes fornecidos com a lista de elementos de energia fornecida na posição de ingrediente fornecida\",\n    \"operator.integrateddynamics.recipe\": \"Receita\",\n    \"operator.integrateddynamics.recipe.basename\": \"Receita %s\",\n    \"operator.integrateddynamics.recipe.input\": \"Ingredientes de Entrada da Receita\",\n    \"operator.integrateddynamics.recipe.input.info\": \"Os ingredientes de entrada da receita fornecida\",\n    \"operator.integrateddynamics.recipe.output\": \"Ingredientes de Saída da Receita\",\n    \"operator.integrateddynamics.recipe.output.info\": \"Os ingredientes de saída da receita fornecida\",\n    \"operator.integrateddynamics.recipe.with_input\": \"Receita com Ingredientes de Entrada\",\n    \"operator.integrateddynamics.recipe.with_input.info\": \"Obtém uma cópia da receita fornecida com os ingredientes fornecidos como entrada\",\n    \"operator.integrateddynamics.recipe.with_output\": \"Receita com Ingredientes de Saída\",\n    \"operator.integrateddynamics.recipe.with_output.info\": \"Obtém uma cópia da receita fornecida com os ingredientes fornecidos como saída\",\n    \"operator.integrateddynamics.recipe.with_input_output\": \"Receita com Ingredientes de Entrada e Saída\",\n    \"operator.integrateddynamics.recipe.with_input_output.info\": \"Cria uma receita com os primeiros ingredientes como entrada e os segundos ingredientes como saída\",\n    \"operator.integrateddynamics.general\": \"Geral\",\n    \"operator.integrateddynamics.general.basename\": \"Geral %s\",\n    \"operator.integrateddynamics.general.choice\": \"Escolha\",\n    \"operator.integrateddynamics.general.choice.info\": \"Se o primeiro valor for verdadeiro, o segundo valor é usado, caso contrário, o terceiro valor.\\\\nO tipo do segundo e terceiro valor deve ser igual.\",\n    \"operator.integrateddynamics.general.identity\": \"Identidade\",\n    \"operator.integrateddynamics.general.identity.info\": \"Uma cópia do valor de entrada.\",\n    \"operator.integrateddynamics.general.constant\": \"Constante\",\n    \"operator.integrateddynamics.general.constant.info\": \"Uma cópia do primeiro valor de entrada.\",\n    \"operator.integrateddynamics.general.isnull\": \"É Nulo\",\n    \"operator.integrateddynamics.general.isnull.info\": \"Se o valor fornecido é nulo\",\n    \"operator.integrateddynamics.general.isnotnull\": \"Não é Nulo\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"Se o valor fornecido não é nulo\",\n    \"operator.integrateddynamics.virtual\": \"Operador Virtual\",\n    \"operator.integrateddynamics.virtual.basename\": \"Virtual: %s\",\n    \"operator.integrateddynamics.virtual.p_conjunction\": \"Conjunção de Predicado\",\n    \"operator.integrateddynamics.virtual.p_conjunction.info\": \"A conjunção de dois predicados\",\n    \"operator.integrateddynamics.virtual.p_disjunction\": \"Disjunção de Predicado\",\n    \"operator.integrateddynamics.virtual.p_disjunction.info\": \"A disjunção de dois predicados\",\n    \"operator.integrateddynamics.virtual.p_negation\": \"Negação de Predicado\",\n    \"operator.integrateddynamics.virtual.p_negation.info\": \"A disjunção de um predicado\",\n    \"operator.integrateddynamics.virtual.piped\": \"Operador em Cadeia (Piped)\",\n    \"operator.integrateddynamics.virtual.piped.info\": \"Uma cadeia de operadores\",\n    \"operator.integrateddynamics.virtual.piped2\": \"Operador em Cadeia 2 (Piped 2)\",\n    \"operator.integrateddynamics.virtual.piped2.info\": \"Uma cadeia de operadores 'piped 2'\",\n    \"operator.integrateddynamics.cast\": \"Conversão (Cast)\",\n    \"operator.integrateddynamics.cast.basename\": \"Conversão\",\n    \"operator.integrateddynamics.cast.tooltip\": \"Converte %s para %s\",\n    \"operator.integrateddynamics.parse\": \"Analisar (Parse)\",\n    \"operator.integrateddynamics.parse.basename\": \"Analisar\",\n    \"operator.integrateddynamics.parse.tooltip\": \"Analisar %s\",\n    \"info_book.integrateddynamics.section.main\": \"Sobre a Dinâmica da Integração\",\n    \"info_book.integrateddynamics.tag_index\": \"&oÍndice&r\",\n    \"info_book.integrateddynamics.introduction\": \"Introdução\",\n    \"info_book.integrateddynamics.introduction.text1\": \"Por muito tempo, as possibilidades de automação foram muito limitadas. O &1Integrated Dynamics&0 visa ser o mod ideal para automatizar seus sistemas lógicos. Ele faz isso fornecendo uma experiênia de programação lógica imersiva e expressiva com ganchos e suporte para a maioria dos elementos do &lMinecraft&r e suas modificações.\",\n    \"info_book.integrateddynamics.introduction.text2\": \"Circuitos de Redstone sempre foram um método muito básico, mas funcional e facilmente compreensível para automação. A basicidade da redstone, no entanto, também é um inconveniente. Para redes de automação com maior complexidade, os circuitos de redstone podem se tornar muito grandes e, consequentemente, difíceis de gerenciar. Além disso, um pequeno vazamento de água pode causar grandes dores de cabeça a qualquer 'redstoner'.\",\n    \"info_book.integrateddynamics.introduction.text3\": \"O objetivo deste livro é duplo. Primeiro, um &ltutorial&r é fornecido para aprender os conceitos mais importantes por trás deste mod. Em segundo lugar, um &lmanual&r detalhado explica todos os elementos e mecanismos para jogadores que não estão interessados no tutorial.\",\n    \"info_book.integrateddynamics.introduction.text4\": \"Você pode navegar por este livro clicando nas setas na parte inferior; clicar com shift irá virar seções inteiras em vez de páginas únicas. No início de grandes seções, você verá um índice com hiperlinks para suas subseções para uma navegação mais rápida. Ao encontrar receitas, os itens explicados neste livro são clicáveis como uma referência rápida para a seção em que são explicados. Você também notará um botão no canto superior esquerdo deste livro, que o levará para a seção acima da atual. O botão no canto superior direito voltará para a página visitada anteriormente a cada clique.\",\n    \"info_book.integrateddynamics.tutorials\": \"Tutoriais\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"Introdução\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"Esta parte do livro contém vários tutoriais que o ajudarão a entender o básico do mod. Além disso, permitirá que você ganhe algumas &lrecompensas&r ao completar as conquistas de cada tutorial.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"Noções Básicas de Menril\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"Para começar com este mod, você precisará encontrar algumas &lMenril Trees&r, que podem ser encontradas mais facilmente em &lMeneglin Biomes&r.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text2\": \"Depois disso, você precisará criar um &lSqueezer&r e uma &lDrying Basin&r para processar &lMenril Logs&r. Isso pode ser feito colocando um bloco de madeira no &lSqueezer&r e pulando nele até que fique plano. Feito isso, a &lMenril Resin&r tentará ir para os dois lados do &lSqueezer&r. Em seguida, coloque uma &lDrying Basin&r ao lado do &lSqueezer&r para que ela se encha com a &lMenril Resin&r que você acabou de espremer.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"Se você quiser repetir o processo, pode reiniciar o &lSqueezer&r aplicando um pulso de redstone.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"No futuro, você também poderá criar variantes aprimoradas dessas máquinas que são mais fáceis de automatizar, mas são mais caras: o &lMechanical Squeezer&r e a &lMechanical Drying Basin&r.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"Fundações de Rede\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"O objetivo principal deste mod é construir redes de controle para automação. Isso é feito usando &lCabos Lógicos&r. Tente fazer uma rede com dez desses cabos.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"O próximo passo será criar alguns &lCartões de Variável&r e &lTransformadores de Variável&r. &lCartões de Variável&r são usados para armazenar referências a valores na rede. &lTransformadores de Variável&r são elementos de criação para componentes que podem ser colocados em &lCabos Lógicos&r para ler e escrever esses valores.\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"Componentes Básicos de Rede\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"Um dos métodos para vincular &lCartões de Variável&r a certos valores é lendo valores de &lLeitores&r. Vamos começar criando alguns leitores básicos. Depois disso, crie um &lPainel de Exibição&r, que é útil para exibir os valores dos &lCartões de Variável&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"Aspectos\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"Como um exemplo simples, vamos tentar ler um valor de redstone e exibi-lo.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text2\": \"Faça isso conectando um &lLeitor de Redstone&r a um &lCabo Lógico&r no mundo. Certifique-se de que o leitor esteja mirando em algo com um valor de redstone, como uma &lTocha de Redstone&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"Depois disso, clique com o botão direito no &lLeitor de Redstone&r para abrir a GUI, após o qual você deve inserir um &lCartão de Variável&r no aspecto &6Valor de Redstone&0 &lInteiro&r, para vincular o valor de redstone do alvo a esse &lCartão de Variável&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text4\": \"Em seguida, coloque um &lPainel de Exibição&r na mesma rede que o &lLeitor de Redstone&r e insira seu &lCartão de Variável&r vinculado para exibir seu valor.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"Como próximo passo, crie um &lEscritor de Redstone&r e faça-o escrever um valor de redstone inteiro máximo que foi lido por um Leitor de Redstone.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"Operações Lógicas\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"Embora exibir valores que se originam de leitores possa ser útil, as coisas ficam realmente interessantes quando você começa a combinar certos valores com outros usando &2Operadores&0. Todos os &2Operadores&0 podem ser encontrados no &lProgramador Lógico&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"Comece criando um &lProgramador Lógico&r, que existe tanto na forma de item quanto de bloco. Abra sua GUI para ver a lista de todos os &loperadores&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"Em seguida, procure pelo operador &6Inteiro&0 e clique nele. Isso abrirá uma janela na qual você deve digitar um número. Quando tiver feito isso, insira um &lCartão de Variável&r vazio para vincular esse &6Inteiro&0 ao &lCartão de Variável&r. Quando terminar, faça isso mais uma vez para outro &6Inteiro&0.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"Finalmente, procure pelo operador de &lAdição&r e abra-o. Isso permite que você some dois &eNúmeros&0. Insira seus dois &lCartões de Variável&r &6Inteiro&0 nesses slots e vincule-o a um novo &lCartão de Variável&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text5\": \"Depois de escrever um valor em um &lCartão de Variável&r, você sempre pode modificá-lo novamente mais tarde, inserindo-o novamente no &lProgramador Lógico&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text6\": \"Como o &1Integrated Dynamics&0 avalia as variáveis de maneira &opreguiçosa&r, este &lCartão de Variável&r de &lAdição&r não contém a adição desses &6Inteiros&0 diretamente, mas a adição só será realizada quando o valor for necessário na rede, com base nas referências das variáveis. Para isso, a rede precisa ser capaz de encontrar esses &lCartões de Variável&r referenciados. Para isso, você precisará criar e colocar um &lArmazenador de Variáveis&r. Insira os dois &lCartões de Variável&r &6Inteiro&0 neste armazenador para torná-los disponíveis para a rede.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text7\": \"Finalmente, agora você pode conectar um cabo ao &lArmazenador de Variáveis&r, anexar um &lPainel de Exibição&r e inserir o &lCartão de Variável&r de &lAdição&r para exibir a soma.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text8\": \"O &lProgramador Lógico&r e os &lArmazenadores de Variáveis&r serão seus melhores amigos quando você quiser fazer automação mais complexa.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"Manuseio de Receitas\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text1\": \"&oReceitas&r são uma grande parte do &lMinecraft&r. É por isso que este mod oferece suporte de primeira classe a elas por meio de valores de &8Receita&0 e valores de &lIngredientes&r para representar suas coleções de entrada e saída. Neste tutorial, veremos como as receitas podem ser obtidas e usadas.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text2\": \"A maneira mais fácil de obter &8Receitas&0 é simplesmente lendo-as de uma máquina que pode manusear receitas. Então, vamos começar lendo todas as receitas de criação colocando um &lLeitor de Máquina&r em uma &lBancada de Trabalho&r e lendo a lista de receitas.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text3\": \"Se você quiser um controle mais refinado sobre as receitas, ou se estiver trabalhando com uma máquina que não expõe suas receitas através do &lLeitor de Máquina&r, você pode criar visualmente uma &8Receita&0 no &lProgramador Lógico&r. Vamos tentar criar uma receita simples, por exemplo, para criar um &lBaú&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text4\": \"Nesta última parte do tutorial, levaremos a leitura de receitas um passo adiante. Usando o aspecto &lReceita por Saída&r, podemos recuperar um operador que, quando aplicado a uma instância de &8Ingredientes&0, retornará a primeira &8Receita&0 que retorna os &8Ingredientes&0 dados como saída. Usaremos isso para procurar a receita de um &lBaú&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text5\": \"Vamos começar criando uma instância de &8Ingredientes&0 com um único &8Baú&0 em seu &lProgramador Lógico&r. Em seguida, leia o aspecto &lReceita por Saída&r de um &lLeitor de Máquina&r que tem como alvo uma &lBancada de Trabalho&r. Depois disso, use o operador &oAplicar&r para aplicar seu &8Ingrediente&0 de &lBaú&r ao operador que você obteve do &lLeitor de Máquina&r. Finalmente, insira a variável resultante em um &lPainel de Exibição&r para ver a receita do &lBaú&r. (Não se esqueça de inserir todas as suas variáveis usadas em um &lArmazenador de Variáveis&r).\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text6\": \"Obviamente, há muito mais que você pode fazer com &8Receitas&0 e &8Ingredientes&0. Dê uma olhada no &lLeitor de Máquina&r e no &lProgramador Lógico&r para descobrir que outra funcionalidade avançada está disponível para você.\",\n    \"info_book.integrateddynamics.tutorials.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.tutorials.nbt.text1\": \"Tags &4NBT&0 são usadas para representar estruturas de dados complexas. Elas são normalmente usadas internamente no Minecraft para armazenar informações de máquinas e entidades. Neste tutorial, abordaremos a leitura e manipulação simples de tags NBT.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text2\": \"Como primeiro passo, use um &lLeitor de Bloco&r em um atril, leia seus dados &4NBT&0 e visualize-os em um &lPainel de Exibição&r.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text3\": \"Em seguida, a partir da tag &4NBT&0 do &lAtril&r lida, leia a sub-tag &3Livro&0 como uma tag composta e visualize-a.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text4\": \"Como passo final, leremos o campo &3id&0 desta sub-tag &3Livro&0. No entanto, em vez de começar pela nossa última sub-tag lida, começaremos pela raiz e aplicaremos uma expressão de &lCaminho NBT&r. Tais expressões são úteis para obter valores &4NBT&0 profundamente aninhados. Por exemplo, aplicar a expressão &4”.root.child1.child2”&0 na tag &3NBT&0 &3{ root: { child1: { child2: ”algum valor” }}}&0 resultará em &3”algum valor”&0. Recursos mais avançados do &lCaminho NBT&r são descritos posteriormente na parte avançada do manual.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text5\": \"Para completar o avanço final, crie uma expressão de &lCaminho NBT&r que seleciona o campo &3Livro&0/&3id&0, aplique-a em nossa tag de &lAtril&r e visualize-a em um &lPainel de Exibição&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"Componentes Avançados de Rede\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"Agora que você está familiarizado com a lógica básica, podemos dar uma olhada nos componentes de rede mais avançados: o &lMaterializador&r e o &lProxy&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"Como você deve se lembrar, o &lProgramador Lógico&r permite que você crie variáveis com valores &oestáticos&r, ou você pode criar variáveis mais complexas baseadas na avaliação de valores &odinâmicos&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"O &lMaterializador&r pode ser usado para converter um valor &odinâmico&r em um &oestático&r. Isso pode ser feito inserindo o &lCartão de Variável&r &odinâmico&r no slot superior do &lMaterializador&r. Todo &lCartão de Variável&r que for inserido no slot esquerdo será definido com o valor que a variável superior tem naquele momento.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"O &lProxy&r permite que você adicione um nível extra de indireção para suas variáveis. Você pode, por exemplo, ter um programa lógico que requer um valor &oestático&r. Se em algum momento no futuro você quiser alterar esse valor, teria que reescrever seu programa, a menos que esse valor estivesse &oproxied&r (intermediado), caso em que você simplesmente teria que atualizar o valor no &lProxy&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"Para este tutorial, crie &lCartões de Variável&r materializados e 'proxied' (intermediados).\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"Operações Avançadas\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"Como você deve ter notado durante o último tutorial, o &lProgramador Lógico&r permite que você faça muito mais do que apenas somar números. Neste tutorial, veremos as estruturas de dados mais avançadas e o encadeamento de operadores.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"Vamos começar criando uma &7Lista&0 estática dos &6Inteiros&0 &61&0, &610&0 e &6100&0, e visualizá-la em um &lPainel de Exibição&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7Itens&0 são um dos possíveis tipos de valor neste mod. Vamos fazer um &lCartão de Variável&r de &7Item&0 estático, recuperar dinamicamente o nome do mod daquele item e visualizá-lo em um &lPainel de Exibição&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"Lembre-se de que você terá que usar um &lArmazenador de Variáveis&r para armazenar sua variável referenciada.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text5\": \"Para a parte final deste tutorial, combinaremos variáveis dinâmicas com operadores. Conecte um &lLeitor de Entidade&r a uma rede, leia a &7Entidade&0 alvo, recupere o &7Bloco&0 que a &7Entidade&0 alvo está olhando e visualize esse &7Bloco&0 em um &lPainel de Exibição&r.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"O Valor dos Operadores\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text1\": \"Em &1Integrated Dynamics&0, &2Operadores&0 são cidadãos de primeira classe. Até agora, nós os usamos apenas manualmente usando o &lProgramador Lógico&r, mas neste tutorial, veremos como eles podem ser usados de uma maneira mais dinâmica, armazenando &2Operadores&0 em &lCartões de Variável&r e invocando-os dinamicamente.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"Como primeiro passo, vá para o &lProgramador Lógico&r e crie um novo &2Operador&0 estático, começando a digitar &oAdição Aritmética&r &2Operador&0 na lista suspensa de preenchimento automático e clicando nele, e escreva-o em um &lCartão de Variável&r. Depois disso, crie duas variáveis &6Inteiro&0. Em seguida, encontre o operador &oAplicar&r e aplique o primeiro &6Inteiro&0 ao &2Operador&0, o que &ovinculará&r o primeiro argumento de entrada do &2Operador&0 de adição a esse &6Inteiro&0, e resultará em um novo &2Operador&0 com um argumento de entrada restante. Em seguida, use o operador &oAplicar&r novamente para &ovincular&r o argumento de entrada final do &2Operador&0 recém-criado. Finalmente, use um &lPainel de Exibição&r para visualizar sua adição invocada dinamicamente.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"Além de aplicar &2Operadores&0, há muitas outras operações avançadas de &2Operador&0. Agora veremos o uso de &2Operadores&0 em &7Listas&0, mais especificamente o &2Operador&0 &oFiltro&r que recebe uma &7Lista&0 e um &oPredicado&r (um &2Operador&0 com um argumento de entrada e uma saída &9Booleana&0). Comece criando uma &7Lista&0 dos &6Inteiros&0 &61&0, &610&0 e &6100&0. Em seguida, armazene o &2Operador&0 &oIgualdade Relacional&r em uma variável. Depois, aplique este operador no &6Inteiro&0 &610&0. Em seguida, use o &2Operador&0 &oFiltro&r com sua &7Lista&0 e o &2Operador&0 aplicado. Isso filtrará todos os itens da &7Lista&0 que retornam &9Falso&0 ao aplicá-los no operador dado, o que neste caso significa que todos os elementos que não são iguais a &610&0 serão removidos da lista. Visualize o resultado em um &lPainel de Exibição&r.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"Existem muitas outras operações em &2Operadores&0, como combinar &oPredicados&r, inverter argumentos de &2Operador&0 e mais operações em &7Listas&0.\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"Desafios\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"Esta seção final de tutoriais contém vários desafios não triviais para mostrar que você entende o básico de &1Integrated Dynamics&0. Nenhuma informação adicional será dada para cada desafio, então boa sorte!\",\n    \"info_book.integrateddynamics.manual\": \"Manual\",\n    \"info_book.integrateddynamics.manual.introduction\": \"Introdução\",\n    \"info_book.integrateddynamics.manual.introduction.text1\": \"Esta parte do livro contém descrições detalhadas de todas as partes do mod.\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"Pré-requisitos\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"Árvores de Menril\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"Você pode ocasionalmente encontrar árvores azuis altas. Tais árvores são chamadas de &lÁrvores de Menril&r e possuem algumas propriedades muito interessantes. Primeiro, as folhas emitem luz. Isso é causado pelas bagas iluminadoras, que também são muito saborosas! Segundo, a madeira de aparência agradável da árvore contém muita resina, que pode ser usada para criar a maioria das coisas neste mod.\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"Bioma Meneglin\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"&lÁrvores de Menril&r são, infelizmente, muito difíceis de encontrar. Felizmente, existe um bioma, o &lBioma Meneglin&r, no qual essas árvores prosperam. A grande quantidade de &lÁrvores de Menril&r faz com que algumas coisas neste bioma se tornem azuis, o que se deve à &lResina de Menril&r que é liberada por essas árvores no ambiente.\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"Espremedor\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer.text1\": \"O &lSqueezer&r é uma máquina de início de jogo que permite pular em blocos e itens para quebrá-los ou espremê-los. Não apenas jogadores, mas qualquer tipo de entidade pode pular ou cair sobre ele para realizar o processo de espremer. Os itens resultantes cairão em qualquer inventário adjacente ou no chão. Os fluidos resultantes permanecerão na máquina e fluirão para um dos dois lados quando houver um tanque alvo. A máquina contém uma pequena suspensão que pode ser ativada com um pulso de redstone para reiniciá-la. Mais notavelmente, esta máquina pode ser usada para espremer a resina das &lToras de Menril&r.\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"Bacia de Secagem\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"Secar blocos e itens pode ser feito usando a &lBacia de Secagem&r. Isso pode ser útil, por exemplo, quando você quer deixar sua &lResina de Menril&r secar para se transformar em &lMenril Cristalizado&r.\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"O que quer que você faça, não insira fluidos quentes!\",\n    \"info_book.integrateddynamics.manual.networks\": \"Redes\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"As &lredes&r do &lIntegrated Dynamics&r são a base de qualquer bom sistema de automação. Uma rede consiste em uma coleção de &lCabos Lógicos&r conectados e peças podem ser colocadas neles. Esses cabos devem ser gerenciados com uma &lChave Inglesa&r. Clicar com shift+botão direito em um cabo ou peça o removerá, enquanto um clique com o botão direito normal o desconectará de outros cabos.\",\n    \"info_book.integrateddynamics.manual.networks.text2\": \"Mesmo que os &lCabos Lógicos&r possam parecer bonitos, em alguns casos você provavelmente vai querer escondê-los. Você pode fazer isso usando &lFachadas&r, que podem se assemelhar a qualquer material. Além disso, esses cabos também são compatíveis com &lMC MultiPart&r, o que significa que você pode esconder seus cabos em estruturas de fachada mais complexas.\",\n    \"info_book.integrateddynamics.manual.logic\": \"Lógica\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"Tipos de Valor\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean\": \"Booleano\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"O tipo de valor mais simples, seus valores podem ser &9Verdadeiro&0 ou &9Falso&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer\": \"Inteiro\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"Um tipo de valor para representar números positivos e negativos. Por exemplo, &6-20&0, &60&0, &61000&0, &6333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"Os valores podem variar entre &6–2147483648&0 e &62147483647&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"Este tipo de valor faz parte da categoria &lNúmero&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double\": \"Dobro\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"Números reais, que também podem ser positivos ou negativos. Por exemplo, &e3.1415&0, &e4815162342&0, &e3.33333333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"Este tipo de valor faz parte da categoria &lNúmero&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long\": \"Longo\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"O mesmo que &6Inteiros&0, mas com um intervalo de valores maior, que vai de &e-9223372036854775808&0 a &e9223372036854775807&0\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"Este tipo de valor faz parte da categoria &lNúmero&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string\": \"String\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"Uma sequência de caracteres, por exemplo &4”aaa”&0, &4“Olá!”&0, &4“x!(àà)ç”&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list\": \"Lista\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list.text1\": \"Uma lista contendo valores de um certo &lTipo de Valor&r. Todos os elementos em uma determinada lista só podem conter valores dos mesmos tipos de valor. Por exemplo &8(0, 1, 2, 3)&0, &8(“a”, “b”, “c”)&0, &8(3.33, 1.14, 5, 6)&0\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator\": \"Operador\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"Um valor contendo uma referência a um &2Operador&0. Os operadores podem ser parcialmente aplicados para avaliações mais flexíveis com base no conceito de &lCurrying&r. Isso faz parte do conceito de &lmetaprogramação&r (pesquise, é uma coisa real!), onde os operadores são tratados como dados por outros operadores. Isso basicamente permite que você crie novos operadores, que podem ser usados para operações mais complexas.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"Você pode, por exemplo, armazenar o &2Operador&0 de adição em um cartão de variável. Faça isso começando a digitar o nome do operador e selecionando-o na lista suspensa de preenchimento automático e clicando nele. Você pode então aplicá-lo parcialmente com um &lCartão de Variável&r &6Inteiro&0, o que resultará em um novo operador virtual com um tipo de entrada e um tipo de saída, que internamente contém uma referência a esse &6Inteiro&0 aplicado. Aplicar outro &6Inteiro&0 resultará em um &6Inteiro&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"Outras operações de &2Operadores&0 estão presentes no &lProgramador Lógico&r para avaliações mais avançadas, como reduzir uma lista a um único valor ou filtrar uma lista com base em um &lPredicado&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt\": \"NBT\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"Certas coisas no &lMinecraft&r usam internamente a estrutura de dados &3NBT&0. Uma tag &3NBT&0 é um tipo especial de objeto que pode conter múltiplos valores de diferentes tipos. Por exemplo, a representação NBT de um &8Baú&0 contendo todos os seus itens, as informações de encantamento de uma &8Picareta&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"As variáveis podem conter referências a tais tags &3NBT&0, e existem operadores correspondentes para ler ou estender essas tags.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"Tags &3NBT&0 são tipicamente usadas neste mod como um mecanismo de fallback de baixo nível, caso você não consiga ler certas informações usando operadores dedicados.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block\": \"Bloco\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"Uma referência a um bloco do &lMinecraft&r. Por exemplo, &8Terra&0, &8Pedregulho&0, &8Argila Tingida de Vermelho&0, &8Baú&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"Item\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"Uma referência a um item do &lMinecraft&r. Por exemplo, &8Maçã&0, &8Pedregulho&0, &8Picareta de Diamante&0, &8Espada Encantada&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"Entidade\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"Uma referência a uma certa entidade do &lMinecraft&r. Por exemplo, &8Zumbi&0, &8Vaca&0, &8Jogador_abc&0 ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"Fluido\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"Uma referência a um fluido do &lMinecraft&r. Por exemplo, &8Lava@1000mB&0, &8Água@3mB&0, &8Resina de Menril@3030mB&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"Ingredientes\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients.text1\": \"Uma coleção de &lItens&r, &lFluidos&r e &lQuantidades de Energia&r (&6Inteiro&0). Seu uso principal é para representar a entrada e saída de &8Receitas&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"Receita\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text1\": \"Uma receita de criação abstrata, que recebe &8Ingredientes&0 como entrada e produz outro valor de &8Ingredientes&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text2\": \"As receitas podem ser criadas visualmente usando o &lProgramador Lógico&r, ou podem ser lidas de máquinas (suportadas) usando o &lLeitor de Máquina&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text3\": \"Essas receitas podem ser usadas para mods de autocrafting como o &lIntegrated Crafting&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text4\": \"Para encontrar rapidamente receitas específicas entre os &lCartões de Variável&r existentes dentro de uma GUI, você pode segurar shift para exibir temporariamente qual é o item de saída da receita.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text5\": \"Clicar com shift em um slot de entrada dentro do &lProgramador Lógico&r permite que você configure propriedades detalhadas deste slot de entrada. Por exemplo, permite indicar se o NBT deve ser considerado ou não, se este item pode ser reutilizado em várias receitas (por exemplo, se não será totalmente consumido após o trabalho de criação) e se alternativas baseadas em tags devem ser consideradas para este item.\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"Variáveis\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"Cartão de Variável\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&lCartões de Variável&r são o principal modo de transporte de lógica neste mod. Uma variável contém uma referência a um certo &lValor&r com um certo &lTipo de Valor&r.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text2\": \"As variáveis podem ser estáticas ou dinâmicas. Variáveis estáticas são definidas uma vez e nunca mudarão depois. Variáveis dinâmicas são o resultado de alguma forma de &lAvaliação Lógica&r, que são feitas por &lOperadores&r ou &lAspectos de Leitor&r. Um exemplo de uma variável dinâmica é o tempo no mundo do &lMinecraft&r, que muda continuamente.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text3\": \"Variáveis dinâmicas só serão avaliadas quando seu valor for solicitado de alguma forma, como exibir o valor em um &lPainel de Exibição&r, ou ser solicitado como parte da avaliação de outra variável dinâmica.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"Cada &lCartão de Variável&r tem um ID único, que é usado internamente em redes para &lAvaliação Lógica&r. Quando um erro ocorre na rede, esses IDs serão mostrados ao jogador para ajudar na depuração. Para melhor usabilidade, uma etiqueta pode ser dada aos &lCartões de Variável&r usando um &lEtiquetador&r, que permitirá aos jogadores ver a etiqueta em vez dos IDs.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"Armazenador de Variáveis\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"Algumas variáveis dinâmicas são avaliadas com base em outras variáveis, onde o acesso a essas variáveis referenciadas deve ser possível. Uma variável dinâmica representando a adição de duas outras variáveis deve ser capaz de acessar essas duas outras variáveis quando é avaliada. O acesso a essas outras variáveis deve ser possível dentro da mesma rede onde a variável dinâmica está sendo avaliada, o que pode ser feito colocando os outros cartões de variável em um &lArmazenador de Variáveis&r. Múltiplos &lArmazenadores de Variáveis&r podem estar presentes em uma rede. Essas outras variáveis também podem referenciar certas variáveis, que também devem estar presentes na mesma rede.\",\n    \"info_book.integrateddynamics.manual.logic.advanced\": \"Avançado\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path\": \"Caminho NBT\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text1\": \"Ao trabalhar com tags &3NBT&0 complexas, você pode rapidamente precisar de cadeias de operadores complexas para ler valores NBT profundamente aninhados. Para simplificar esta tarefa, expressões de &lCaminho NBT&r baseadas em &4String&0 podem ser usadas para determinar o caminho dentro de uma tag &3NBT&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text2\": \"A expressão de Caminho NBT mais simples é composta por uma cadeia de &lseletores de campo&r, como &4.fieldName&0 ou &4[”fieldName”]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text3\": \"Por exemplo, aplicar a expressão &4”.root.child1.child2”&0 na tag &3NBT&0 &3{ root: { child1: { child2: ”algum valor” }}}&0 resultará em &3”algum valor”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text4\": \"Múltiplos campos podem ser selecionados fornecendo um array de nomes de campos ou um curinga, por exemplo &4[fieldA,fieldB]&0 ou &4*&0\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text5\": \"Por exemplo, aplicar a expressão &4”.a[b1,b2]*”&0 na tag &3NBT&0 &3{ a: { b1: { c: ”algum valor” }}}&0 resultará em &3”algum valor”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text6\": \"Ao trabalhar com listas NBT, entradas específicas podem ser selecionadas especificando um índice, como &4[1]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text7\": \"Por exemplo, aplicar a expressão &4”.a[1]”&0 na tag &3NBT&0 &3{ a: [10,20] }&0 resultará em &3”20”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text8\": \"Múltiplos índices de lista podem ser selecionados usando o operador de fatia: &4[início:fim:passo]&0. (Apenas o início é obrigatório, os outros elementos podem ser omitidos)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text9\": \"Por exemplo, aplicar a expressão &4”.a[1:4:2]”&0 na tag &3NBT&0 &3{ a: [0,1,2,3,4,5] }&0 resultará em &3[1,3]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text10\": \"Expressões de filtro mais avançadas também são possíveis usando a sintaxe de filtro, por exemplo &4[?(@.childName < 10)]&0 (@ se refere à tag atual, .. se refere à tag pai, $ se refere à tag raiz)\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text11\": \"Por exemplo, aplicar a expressão &4”$.a[?(@ == 3)]”&0 na tag &3NBT&0 &3{ a: [0,1,2,3,4,5] }&0 resultará em &3[3]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text12\": \"Expressões podem ser combinadas usando ”&&” (e), ”||” (ou) e ”!” (não).\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text13\": \"Por exemplo, aplicar a expressão &4”$.a[?(@ == 3 || (@ == 5))]”&0 na tag &3NBT&0 &3{ a: [0,1,2,3,4,5] }&0 resultará em &3[3,5]&0.\",\n    \"info_book.integrateddynamics.manual.parts\": \"Peças\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"Introdução\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text1\": \"Peças podem ser colocadas em cada lado de um &lCabo Lógico&r. Esta seção descreve peças que podem ler &lValores&r e torná-los disponíveis com um &lCartão de Variável&r, e peças que podem realizar ações com base nos &lValores&r dentro dos &lCartões de Variável&r.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text2\": \"A maioria das peças tem configurações configuráveis, que podem ser acessadas usando o botão no canto superior esquerdo na GUI de uma peça. Isso permite que você altere os &lTicks/Operação&r da peça, ou seja, a cada quantos ticks esta peça deve realizar suas ações. Em segundo lugar, a &lPrioridade&r da peça pode ser alterada, o que indica a ordem em que esta peça pode agir em relação às outras peças na mesma rede no mesmo tick. Se a peça A tem uma prioridade de -1, e a peça B tem uma prioridade de 1, então a peça B sempre realizará suas ações antes da peça A. Se as prioridades das peças forem as mesmas, então a ordem relativa é aleatória.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text3\": \"O &lCanal de Energia&r de uma peça determina de onde ela pode extrair sua energia. -1 é o canal curinga (corresponderá a todos os canais), e 0 é o canal padrão (usado por peças como células de energia que não têm configuração de canal). Finalmente, o &lLado Alvo&r determina com qual lado de seu bloco alvo será interagido. O padrão é o lado oposto ao que ele está realmente colocado.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text4\": \"Algumas peças de leitor e escritor só contêm aspectos quando certos outros mods estão presentes. Se nenhum aspecto estiver presente para uma determinada peça, um aviso será adicionado à dica de ferramenta do item sempre que você tentar criá-lo.\",\n    \"info_book.integrateddynamics.manual.parts.settings\": \"Configurações\",\n    \"info_book.integrateddynamics.manual.parts.settings.text1\": \"A maioria das peças tem uma tela de &lConfigurações&r através da qual as configurações básicas da peça podem ser modificadas.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text2\": \"O Lado Alvo é o lado com o qual esta peça irá interagir. O padrão é o oposto do lado do cabo em que esta peça foi colocada.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text3\": \"Os Ticks/Operação permitem que você configure a frequência de tique desta peça. Quanto maior, mais lento ela opera.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text4\": \"A Prioridade determina a ordem em que esta peça é executada dentro de um único tique de rede.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text5\": \"O Canal de Energia indica o canal do qual a energia deve ser consumida quando esta peça tica. Isso só é aplicável se o consumo de energia para redes estiver habilitado.\",\n    \"info_book.integrateddynamics.manual.parts.offsets\": \"Deslocamentos\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text1\": \"Por padrão, as peças terão como alvo a posição vizinha direta. No entanto, quando &lAprimoramentos de Peça&r são aplicados, a posição alvo pode ser alterada através da tela de &lDeslocamento&r da peça.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text2\": \"Os deslocamentos são sempre relativos à posição alvo da peça e são limitados pelo número de Aprimoramentos de Peça que foram aplicados.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text3\": \"Os deslocamentos podem ser definidos manualmente ou visualmente usando a &lChave Inglesa&r. Ao definir a &lChave Inglesa&r para o modo de deslocamento, uma posição pode ser armazenada com shift + clique direito. Esta posição pode ser salva em peças clicando com o botão direito nelas com essa &lChave Inglesa&r.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text4\": \"Os deslocamentos não podem ser definidos apenas estaticamente, mas também podem ser &odinâmicos&r usando &lCartões de Variável&r.\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"Leitores\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"Introdução\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction.text1\": \"O método mais simples para criar uma variável dinâmica é usando &lLeitores&r. Diferentes tipos de leitores existem para ler informações do mundo e armazená-las dentro de um &lCartão de Variável&r. Cada &lLeitor&r contém um ou mais &lAspectos&r para ler coisas diferentes com diferentes &lTipos de Valor&r.\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"Áudio\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"Informações sobre música e áudio que estão tocando no ambiente.\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"Bloco\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"Informações sobre o bloco que a peça está de frente.\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"Entidade\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"Informações sobre a entidade ou entidades que estão dentro do quadrado que a peça está de frente.\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"Extra-Dimensão\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"Informações sobre o servidor, como a lista de jogadores online.\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"Fluido\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"Lê valores do tanque de fluido que esta peça está de frente.\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"Inventário\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"Lê informações do inventário à frente, como a quantidade de slots e itens. Isso não transferirá os itens em si.\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"Máquina\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"A máquina que esta peça está de frente pode ser lida, como o trabalho que estão fazendo.\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"Rede\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"Lê informações sobre a rede &lIntegrated Dynamics&r que esta peça está de frente. Esta não precisa ser a mesma rede em que esta peça está.\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"Redstone\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"Lê informações de redstone do bloco à frente.\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"Mundo\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"Informações sobre o mundo em que esta peça está, como o clima e a hora atuais.\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"Escritores\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"Áudio\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"Toca sons e músicas.\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"Efeito\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"Emite partículas com base em certos valores e configurações.\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"Entidade\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"Configura e controla entidades.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"Máquina\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"Configura e controla máquinas.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"Inventário\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"Realiza ações relacionadas a inventários e tubos. Isso não transferirá os itens em si.\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"Redstone\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"Emite um certo nível de redstone.\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"Outros\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"Luz\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"Uma luz simples que está sempre ligada ou pode ser controlada usando um &lCartão de Variável&r &1Booleano&0 ou &6Inteiro&0.\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"Painel de Exibição\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"Uma ferramenta útil de monitoramento ou depuração que simplesmente mostra o valor de um &lCartão de Variável&r. &lPainéis de Exibição&r podem ser girados clicando com o botão direito com uma &lChave Inglesa&r.\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text2\": \"Dentro da GUI, um botão &lCopiar&r está disponível para copiar o valor atualmente exibido para sua área de transferência para que possa ser usado fora do jogo. Você também pode usar a tecla de atalho de cópia padrão do seu SO para copiar este valor.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"Conector Monodirecional\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"Às vezes, suas redes lógicas se estenderão por grandes distâncias. Embora cabos regulares resolvam o problema, pode não ser muito conveniente colocar cabos por uma longa distância.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&lConectores Monodirecionais&r permitem conectar redes sem fio a longa distância. Isso pode ser feito colocando um primeiro conector em uma rede e um segundo conector em outra rede. Esses dois conectores devem estar em linha reta um com o outro, com uma distância menor ou igual a 512 blocos. Os conectores aparecerão ativos quando uma conexão válida for estabelecida.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"Conector Onidirecional\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"Embora os conectores &lMonodirecionais&r sejam úteis para conexões de longa distância em uma única direção, em alguns casos isso pode ser um pouco limitado.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"Você pode, por exemplo, querer conectar mais de duas redes usando um único conector ou conectar-se a redes em outras dimensões. Para esses casos, você pode usar os conectores &lOnidirecionais&r.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"Esses conectores terão um ID de grupo único quando criados pela primeira vez, que identifica o grupo no qual as redes podem ser conectadas. Cada grupo de conectores tem uma cor única de partículas quando colocado no mundo, o ID do grupo pode ser visto na dica de ferramenta do item ou clicando com o botão direito no conector quando colocado. Conectores adicionais podem ser adicionados a um grupo criando um novo usando um conector existente dentro de um grupo.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"Você também pode alterar o grupo de conectores existentes de duas maneiras. Criar um sozinho em uma mesa de trabalho o atribuirá a um novo grupo sem outros membros, e criar dois juntos copiará o grupo do primeiro para o segundo.\",\n    \"info_book.integrateddynamics.manual.machines\": \"Máquinas\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"Materializador\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"Para tornar estático um &lCartão de Variável&r dinâmico, um &lMaterializador&r pode ser usado. O &lCartão de Variável&r dinâmico deve ser inserido, e um novo &lCartão de Variável&r vazio deve ser inserido, o que fará com que ele contenha o valor atual do &lCartão de Variável&r dinâmico como um valor estático.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"Isso pode ser útil em situações em que você deseja registrar os valores de um determinado &lCartão de Variável&r dinâmico, ou quando não precisa mais da avaliação dinâmica de um &lCartão de Variável&r.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&lCartões de Variável&r podem ser inseridos e retirados do &lMaterializador&r por meio de canos/automação.\",\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"Proxy\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"Um &lProxy&r pode ser usado para fazer uma referência dinâmica a um determinado &lCartão de Variável&r. Ele permite que você insira um &lCartão de Variável&r no slot superior e escreva um novo &lCartão de Variável&r, que assumirá dinamicamente o valor do &lCartão de Variável&r que está presente no proxy. Note que o &lCartão de Variável&r referenciado (ou qualquer outro) deve permanecer no slot superior para que o proxy funcione.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"Isso é útil em situações em que você tem uma cadeia de &lCartões de Variável&r, mas ainda gostaria de poder alterar um valor manualmente. Você poderia então intermediar (proxy) essa variável, o que permitirá que ela seja trocada por outra variável a qualquer momento.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"Assim como com o &lMaterializador&r, &lCartões de Variável&r podem ser inseridos e retirados dele por meio de canos/automação.\",\n    \"info_book.integrateddynamics.manual.machines.delay\": \"Atrasador\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"Em algumas situações, você pode precisar manter algum tipo de histórico de valores de variáveis. Por exemplo, quando você quer calcular a média ou a diferença dos últimos valores.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text2\": \"Para isso, você pode usar um &lAtrasador&r, com o qual pode manter o histórico dos valores de uma determinada variável. Você pode fazer isso inserindo um &lCartão de Variável&r no slot superior para o qual deseja manter o histórico. Agora você pode criar &lCartões de Variável&r que se referirão a uma lista de valores, que corresponde ao histórico. O &lCartão de Variável&r referenciado (ou qualquer outro) deve permanecer no slot superior para que o atrasador funcione.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text3\": \"Os valores mais recentes correspondem aos últimos elementos na lista. Você pode escolher o comprimento máximo do histórico dentro da gui.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"Bateria de Energia\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"Redes do &lIntegrated Dynamics&r podem ser configuradas para consumir energia. Para isso, você pode conectar &lBaterias de Energia&r à rede para adicionar capacidade de armazenamento de energia a ela.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"Aplicar um sinal de redstone ativará o modo de exportação automática de energia para todos os lados.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"Quando esta bateria é mantida em seu inventário, o modo de &lfornecimento automático&r pode ser ativado com shift + clique direito. Quando ativado, esta bateria tentará preencher automaticamente com energia os itens que você está segurando em suas duas mãos.\",\n    \"info_book.integrateddynamics.manual.machines.generator\": \"Gerador\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"Espremedor Mecânico\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer.text1\": \"Chegará um momento em que você se cansará de pular em &lSqueezers&r. Se até lá você não tiver automatizado seus &lSqueezers&r, talvez seja hora de pensar em fazer um &lEspremedor Mecânico&r. Ele não requer nenhum esforço manual, mas precisa de energia para funcionar. Isso também o torna significativamente mais rápido que os &lEspremedores&r regulares e permite que mais coisas sejam espremidas com um rendimento maior.\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"Bacia de Secagem Mecânica\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"A secagem com a &lBacia de Secagem&r pode demorar um pouco. Se você quer que as coisas vão mais rápido, ou se quer ser capaz de secar mais coisas, pode usar a &lBacia de Secagem Mecânica&r, que requer energia para funcionar.\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"Programação Lógica\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"Outro método para criar cartões de variável dinâmicos é aplicando um &lOperador&r em um ou mais &lCartões de Variável&r para produzir um novo &lCartão de Variável&r. Um exemplo de &lOperador&r é a adição de dois &lNúmeros&r, resultando em um novo &lNúmero&r.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text2\": \"Um &lOperador&r pode ter um ou mais valores de entrada e tem um valor de saída. Na maioria dos casos, as entradas e a saída têm um certo &lTipo de Valor&r, caso contrário, terão o tipo de valor &lQualquer&r. Um &lOperador&r com uma única entrada e uma saída &lBooleana&r é chamado de &lPredicado&r.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"O &lProgramador Lógico&r contém uma grande coleção de &lOperadores&r. Ele também permite que você crie &lCartões de Variável&r estáticos para todos os &lTipos de Valor&r. Depois de selecionar um &lOperador&r, &lCartões de Variável&r válidos devem ser inseridos nos slots de entrada esperados. Finalmente, outro &lCartão de Variável&r deve ser inserido no slot de saída. Se o &lOperador&r for válido, uma nova variável dinâmica será escrita no cartão. Lembre-se de que os &lCartões de Variável&r referenciados devem estar presentes em um &lArmazenador de Variáveis&r na mesma rede onde o &lOperador&r será avaliado.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text4\": \"Além de escrever valores nos &lCartões de Variável&r, o &lProgramador Lógico&r também pode ser usado para modificar &lCartões de Variável&r existentes. O valor de um &lCartão de Variável&r existente pode ser carregado no &lProgramador Lógico&r inserindo-o no slot do lado direito, garantindo que nenhum &lOperador&r ou &lTipo de Valor&r no lado esquerdo esteja selecionado (o que é o caso ao abrir o &lProgramador Lógico&r pela primeira vez, ou o elemento atual pode ser limpo).\",\n    \"info_book.integrateddynamics.manual.logic_programming.text5\": \"O &lProgramador Lógico&r permite que você pesquise na lista de &lOperadores&r por nome usando a caixa de texto na parte superior. Ele também permite filtrar por tipos de valor de entrada ou saída, inserindo um &lCartão de Variável&r desses tipos nos slots inferiores esquerdos.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text6\": \"Se um &lEtiquetador&r estiver presente no inventário do jogador, o &lCartão de Variável&r resultante pode ser etiquetado imediatamente clicando no botão 'E' ao lado do slot de saída.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text7\": \"O &lProgramador Lógico&r existe tanto na forma de bloco quanto de item. Ambos podem ser trocados colocando-os em uma grade de criação.\",\n    \"info_book.integrateddynamics.manual.diagnostics\": \"Diagnóstico de Rede\",\n    \"info_book.integrateddynamics.manual.diagnostics.text1\": \"Jogadores com status de operador têm acesso à ferramenta de &lDiagnóstico de Rede&r, que permite inspecionar o tempo de tique em todas as redes no servidor.\",\n    \"info_book.integrateddynamics.manual.diagnostics.text2\": \"Para habilitar esta ferramenta, o comando &o\\\\integrateddynamics networkdiagnostics&r pode ser executado, o que iniciará um servidor web local temporário.\",\n    \"info_book.integrateddynamics.manual.diagnostics.text3\": \"Ao clicar no link mostrado após executar o comando, seu navegador da Web será aberto e mostrará uma tabela contendo todas as redes e seus elementos. Você pode clicar em qualquer linha para destacá-los no mundo, ou clicar no botão &lTeleportar&r para teleportar seu jogador para eles.\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/pt_pt.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/ro_ro.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/ru_ru.json",
    "content": "{\n    \"_comment\": \"------ О содержании динамики интеграции ------\",\n    \"itemGroup.integrateddynamics\": \"Интегрированная динамика\",\n    \"general.integrateddynamics.description\": \"Возьмите полный и автоматизированный контроль над вашими приборами.\",\n    \"general.integrateddynamics.info.none\": \"Нет\",\n    \"general.integrateddynamics.energy\": \"Энергия\",\n    \"general.integrateddynamics.error.no_network\": \"Сеть не найдена\",\n    \"general.integrateddynamics.true\": \"Истина\",\n    \"general.integrateddynamics.false\": \"Ложь\",\n    \"gui.integrateddynamics.part_settings\": \"Настройки части\",\n    \"gui.integrateddynamics.aspect_settings\": \"Настройки аспекта\",\n    \"gui.integrateddynamics.part_offsets\": \"Смещения части\",\n    \"gui.integrateddynamics.keybinding\": \"Привязка клавиш\",\n    \"gui.integrateddynamics.button.save\": \"Сохранить\",\n    \"gui.integrateddynamics.button.rename\": \"Переименовать\",\n    \"gui.integrateddynamics.button.copy\": \"Копировать\",\n    \"gui.integrateddynamics.button.copy.info\": \"Скопировать в буфер обмена\",\n    \"gui.integrateddynamics.button.edit\": \"Редактировать\",\n    \"gui.integrateddynamics.button.add\": \"Добавить\",\n    \"gui.integrateddynamics.button.remove\": \"Удалить\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"Тиков/Операция\",\n    \"gui.integrateddynamics.partsettings.priority\": \"Приоритет\",\n    \"gui.integrateddynamics.partsettings.channel\": \"Энергетический канал\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"Потребление энергии сети отключено на этом сервере.\",\n    \"gui.integrateddynamics.partsettings.side\": \"Целевая сторона\",\n    \"gui.integrateddynamics.partoffset.offsets\": \"Смещение относительно целевой позиции\",\n    \"gui.integrateddynamics.partoffset.offsets.max\": \"Максимальное смещение: %s\",\n    \"gui.integrateddynamics.partoffset.offsets.max.howtoincrease\": \"Увеличьте лимит с помощью Улучшения части: Смещение\",\n    \"gui.integrateddynamics.partoffset.slot.message.noint\": \"Недопустимая целочисленная координата\",\n    \"gui.integrateddynamics.partoffset.slot.message.outofrange\": \"Смещение превышает допустимый диапазон\",\n    \"gui.integrateddynamics.partoffset.x\": \"Смещение по X\",\n    \"gui.integrateddynamics.partoffset.y\": \"Смещение по Y\",\n    \"gui.integrateddynamics.partoffset.z\": \"Смещение по Z\",\n    \"gui.integrateddynamics.delay.capacity\": \"Вместимость\",\n    \"gui.integrateddynamics.part.properties\": \"Свойства:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"Фильтр\",\n    \"gui.integrateddynamics.rewards\": \"Награды\",\n    \"gui.integrateddynamics.rewards.collect\": \"Получить\",\n    \"gui.integrateddynamics.rewards.collected\": \"Получено\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"Не удается получить награды, ваш инвентарь полон?\",\n    \"gui.integrateddynamics.input\": \"Ввод: %s\",\n    \"gui.integrateddynamics.output\": \"Вывод: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"Автовыброс жидкости\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Автовыброс жидкости: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"Передача в соседние резервуары.\",\n    \"gui.integrateddynamics.input_short\": \"Ввод\",\n    \"gui.integrateddynamics.output_short\": \"Вывод\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"Строгое NBT\",\n    \"gui.integrateddynamics.recipe.tagvariants\": \"Варианты тегов предметов\",\n    \"gui.integrateddynamics.recipe.reusable\": \"Многоразовый\",\n    \"gui.integrateddynamics.operator.globalname\": \"Глобальное имя: %s\",\n    \"gui.integrateddynamics.operator.localname\": \"Имя метода: %s\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Integrated Dynamics - Диагностика сети\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"Части\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"Наблюдатели за хранилищем\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Сеть\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Кабели\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"Часть\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Время тика (мс)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"Измерение\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"Позиция\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"Сторона\",\n    \"gui.integrateddynamics.diagnostics.table.actions\": \"Действия\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"Фокус поиска в Логическом программисте\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"Переименовать переменную в Логическом программисте\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"Переворот страницы\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"Переворот нескольких страниц\",\n    \"block.integrateddynamics.cable\": \"Логический кабель\",\n    \"block.integrateddynamics.variablestore\": \"Хранилище переменных\",\n    \"block.integrateddynamics.energy_battery\": \"Энергетическая батарея\",\n    \"block.integrateddynamics.energy_battery.info\": \"Поместите в сетку крафта с другими Энергетическими батареями для увеличения ёмкости. Shift + Правый клик для авто-подачи.\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"Авто-подача: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"Креативная энергетическая батарея\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"Авто-подача: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"Логический программист\",\n    \"block.integrateddynamics.coal_generator\": \"Генератор\",\n    \"block.integrateddynamics.coal_generator.info\": \"Производит энергию из топливных предметов\",\n    \"block.integrateddynamics.proxy\": \"Прокси\",\n    \"block.integrateddynamics.proxy.info\": \"Предоставляет ссылку на переменную как аспект. Очистите в сетке крафта.\",\n    \"block.integrateddynamics.materializer\": \"Материализатор\",\n    \"block.integrateddynamics.materializer.info\": \"Материализует значение переменной в новую переменную\",\n    \"block.integrateddynamics.menril_log\": \"Менриловое бревно\",\n    \"block.integrateddynamics.menril_log_filled\": \"Обогащённое менриловое бревно\",\n    \"block.integrateddynamics.menril_log_stripped\": \"Очищенное менриловое бревно\",\n    \"block.integrateddynamics.menril_leaves\": \"Менриловые листья\",\n    \"block.integrateddynamics.menril_sapling\": \"Менриловая саженец\",\n    \"block.integrateddynamics.menril_planks\": \"Менриловые доски\",\n    \"block.integrateddynamics.menril_slab\": \"Менриловая плита\",\n    \"block.integrateddynamics.menril_fence\": \"Менриловый забор\",\n    \"block.integrateddynamics.menril_fence_gate\": \"Менриловая калитка\",\n    \"block.integrateddynamics.menril_wood\": \"Менриловое дерево\",\n    \"block.integrateddynamics.menril_wood_stripped\": \"Очищенное менриловое дерево\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Блок кристаллизованного менрила\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"Кирпич кристаллизованного менрила\",\n    \"block.integrateddynamics.block_menril_resin\": \"Менриловая смола\",\n    \"block.integrateddynamics.drying_basin\": \"Сушильный бассейн\",\n    \"block.integrateddynamics.drying_basin.info\": \"Для сушки предметов и жидкостей\",\n    \"block.integrateddynamics.squeezer\": \"Пресс\",\n    \"block.integrateddynamics.squeezer.info\": \"Сжимает, сплющивает и дробит при прыжке на него. Импульс красного камня для сброса.\",\n    \"block.integrateddynamics.menril_torch\": \"Менриловый факел\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Каменный менриловый факел\",\n    \"block.integrateddynamics.invisible_light\": \"Невидимый свет\",\n    \"block.integrateddynamics.menril_door\": \"Менриловая дверь\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Менриловые ступени\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Ступени из кристаллизованного менрила\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"Ступени из кирпича кристаллизованного менрила\",\n    \"block.integrateddynamics.delay\": \"Задержка\",\n    \"block.integrateddynamics.delay.info\": \"Предоставляет историю переменной как аспект. Очистите в сетке крафта.\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"Жидкий хорус\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"Блок кристаллизованного хоруса\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"Кирпич кристаллизованного хоруса\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"Ступени из кристаллизованного хоруса\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"Ступени из кирпича кристаллизованного хоруса\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"Механический пресс\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"Сжимает, сплющивает и дробит с использованием энергии.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"Механический сушильный бассейн\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"Для сушки предметов и жидкостей с использованием энергии.\",\n    \"block.integrateddynamics.crystalized_menril_block_slab\": \"Плита из кристаллизованного менрила\",\n    \"block.integrateddynamics.crystalized_menril_brick_slab\": \"Плита из кирпича кристаллизованного менрила\",\n    \"block.integrateddynamics.crystalized_chorus_block_slab\": \"Плита из кристаллизованного хоруса\",\n    \"block.integrateddynamics.crystalized_chorus_brick_slab\": \"Плита из кирпича кристаллизованного хоруса\",\n    \"item.integrateddynamics.wrench\": \"Гаечный ключ\",\n    \"item.integrateddynamics.wrench.info\": \"Shift + Правый клик в воздухе для смены режима\",\n    \"item.integrateddynamics.wrench.mode\": \"§eРежим: §r%s\",\n    \"item.integrateddynamics.wrench.mode.default\": \"По умолчанию\",\n    \"item.integrateddynamics.wrench.mode.default.info\": \"Удаление частей или поворот блоков\",\n    \"item.integrateddynamics.wrench.mode.offset\": \"Сохранить смещение\",\n    \"item.integrateddynamics.wrench.mode.offset.info\": \"Shift + Правый клик по позиции и Правый клик по части для связывания смещения\",\n    \"item.integrateddynamics.wrench.mode.offset.saved\": \"Позиция сохранена в гаечном ключе: %s\",\n    \"item.integrateddynamics.wrench.mode.offset.success\": \"Новая позиция смещения установлена в части\",\n    \"item.integrateddynamics.wrench.mode.offset.incomplete\": \"Позиция ещё не сохранена в этот гаечный ключ\",\n    \"item.integrateddynamics.wrench.mode.offset.fail\": \"Не удалось установить смещение. Увеличьте максимальное смещение с помощью Улучшений!\",\n    \"item.integrateddynamics.wrench.mode.offset.pos\": \"Позиция: %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side\": \"Сохранить смещение и сторону\",\n    \"item.integrateddynamics.wrench.mode.offset_side.info\": \"Shift + Правый клик по позиции и Правый клик по части для связывания смещения и стороны\",\n    \"item.integrateddynamics.wrench.mode.offset_side.saved\": \"Позиция и сторона сохранены в гаечном ключе: %s - %s\",\n    \"item.integrateddynamics.wrench.mode.offset_side.success\": \"Новая позиция смещения и сторона установлены в части\",\n    \"item.integrateddynamics.wrench.mode.offset_side.side\": \"Сторона: %s\",\n    \"item.integrateddynamics.variable\": \"Карта переменной\",\n    \"item.integrateddynamics.variable.info\": \"Очистите или скопируйте в сетке крафта\",\n    \"item.integrateddynamics.variable.warning\": \"§4§lВНИМАНИЕ: НЕ копируйте этот предмет средней кнопкой мыши!\",\n    \"item.integrateddynamics.variable.id\": \"§e§oID переменной: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"Метчик\",\n    \"item.integrateddynamics.labeller.info\": \"Метит ваши переменные. Также работает в Логическом программисте.\",\n    \"item.integrateddynamics.labeller.button.write\": \"Записать\",\n    \"item.integrateddynamics.facade\": \"Фасад\",\n    \"item.integrateddynamics.facade.info\": \"Объедините с блоком в таблице крафта\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"Ведро жидкого хоруса\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"Ведро менриловой смолы\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"Кусок кристаллизованного менрила\",\n    \"item.integrateddynamics.variable_transformer\": \"Входные/Выходные трансформаторы переменных\",\n    \"item.integrateddynamics.variable_transformer_output\": \"Выходной трансформатор переменных\",\n    \"item.integrateddynamics.variable_transformer_input\": \"Входной трансформатор переменных\",\n    \"item.integrateddynamics.menril_berries\": \"Ягоды менрила\",\n    \"item.integrateddynamics.menril_berries.info\": \"Вкусно и светится\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"Портативный логический программист\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"О динамике интеграции\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"Полное руководство по моду с учебниками\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"Кусок кристаллизованного хоруса\",\n    \"item.integrateddynamics.logic_director\": \"Логический директор\",\n    \"item.integrateddynamics.proto_chorus\": \"Прото-хорус\",\n    \"item.integrateddynamics.enhancement_offset\": \"Улучшение части: Смещение\",\n    \"item.integrateddynamics.enhancement_offset.info\": \"Увеличивает максимальное смещение части при правом клике\",\n    \"item.integrateddynamics.enhancement_offset.tooltip\": \"Увеличение смещения: %s\",\n    \"item.integrateddynamics.enhancement_offset.increased\": \"Смещение части увеличено до %s\",\n    \"item.integrateddynamics.enhancement_offset.limit\": \"Смещение части уже достигло предела (%s)\",\n    \"fluid_type.integrateddynamics.menril_resin\": \"Менриловая смола\",\n    \"fluid_type.integrateddynamics.liquid_chorus\": \"Жидкий хорус\",\n    \"biome.integrateddynamics.meneglin\": \"Менеглин\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"Открытие Менеглина\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"Срубите дерево менрила, которое легче всего найти в биомах Менеглина.\",\n    \"advancement.integrateddynamics.squeezing\": \"Сжатие как босс\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"Создайте пресс.\",\n    \"advancement.integrateddynamics.drying\": \"Сушка как босс\",\n    \"advancement.integrateddynamics.drying.desc\": \"Создайте сушильный бассейн.\",\n    \"advancement.integrateddynamics.menril_production\": \"Производство менрила\",\n    \"advancement.integrateddynamics.menril_production.desc\": \"Обработайте менриловое бревно для получения менриловой смолы и высушите её, чтобы получить кристаллизованную менриловую смолу.\",\n    \"advancement.integrateddynamics.cables_logic\": \"Кабели для логики\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"Создайте логический кабель.\",\n    \"advancement.integrateddynamics.networks_logic\": \"Сети для логики\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"Постройте логическую сеть с минимум 10 подключенными логическими кабелями.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"Менриловый гаечный ключ\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"Создайте гаечный ключ.\",\n    \"advancement.integrateddynamics.variables\": \"Переменные для значений\",\n    \"advancement.integrateddynamics.variables.desc\": \"Создайте карту переменной.\",\n    \"advancement.integrateddynamics.variable_input\": \"Трансформация входных переменных\",\n    \"advancement.integrateddynamics.variable_output\": \"Трансформация выходных переменных\",\n    \"advancement.integrateddynamics.redstone_reading\": \"Чтение красного камня\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"Создайте считыватель красного камня.\",\n    \"advancement.integrateddynamics.block_reading\": \"Чтение блоков\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"Создайте считыватель блоков.\",\n    \"advancement.integrateddynamics.inventory_reading\": \"Чтение инвентаря\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"Создайте считыватель инвентаря.\",\n    \"advancement.integrateddynamics.machine_reading\": \"Чтение машин\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"Создайте считыватель машин.\",\n    \"advancement.integrateddynamics.value_displaying\": \"Отображение значений\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"Создайте дисплейную панель.\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"Захват сигнала красного камня\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"Создайте карту переменной для значения красного камня типа §6Целое число§7.\",\n    \"advancement.integrateddynamics.redstone_observement\": \"Наблюдение за красным камнем\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"Поместите карту переменной типа §6Целое число§7 в дисплейную панель.\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"Передача сигнала красного камня\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"Заставьте записывающее устройство красного камня записать максимальный уровень сигнала красного камня, считанный считывателем красного камня.\",\n    \"advancement.integrateddynamics.recipe_reading\": \"Чтение рецептов\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"Прочитайте список рецептов из машины (например, верстака) с помощью считывателя машин.\",\n    \"advancement.integrateddynamics.recipe_creation\": \"Создание рецептов\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"Визуально создайте §8Рецепт§7 с помощью логического программиста.\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"Поиск рецептов\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"Динамически найдите рецепт сундука с помощью считывателя машин.\",\n    \"advancement.integrateddynamics.lectern_data\": \"NBT данных кафедры\",\n    \"advancement.integrateddynamics.lectern_data.desc\": \"Прочитайте NBT-тег из машины с помощью считывателя блоков, например, кафедры.\",\n    \"advancement.integrateddynamics.lectern_book\": \"NBT книги на кафедре\",\n    \"advancement.integrateddynamics.lectern_book.desc\": \"Прочитайте поле NBT книги внутри блока кафедры из NBT-тега, считанного считывателем блоков.\",\n    \"advancement.integrateddynamics.lectern_book_name\": \"Имя книги NBT на кафедре\",\n    \"advancement.integrateddynamics.lectern_book_name.desc\": \"Прочитайте имя (id) книги внутри блока кафедры, применив выражение пути NBT к NBT-тегу, считанному считывателем блоков.\",\n    \"advancement.integrateddynamics.logic_programming\": \"Логическое программирование\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"Откройте графический интерфейс логического программиста.\",\n    \"advancement.integrateddynamics.constant_definition\": \"Определение константы\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"Определите карту переменной типа §6Целое число§7.\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"Арифметическое сложение\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"Сложите два §eчисла§7 и отобразите результат.\",\n    \"advancement.integrateddynamics.variable_materialization\": \"Материализация переменной\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"Материализуйте переменную в §lматериализаторе§r.\",\n    \"advancement.integrateddynamics.variable_proxying\": \"Проксирование переменной\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"Создайте действительную прокси-переменную в §lпрокси§r.\",\n    \"advancement.integrateddynamics.logical_list_building\": \"Построение логического списка\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"Создайте статический список с §6целыми числами§7 1, 10 и 100.\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"Идентификация происхождения предмета\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"Узнайте название мода для предмета.\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"На что я смотрю?\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"Получите блок, на который вы смотрите.\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"Динамические добавления\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"Вызовите оператор сложения динамически.\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"Динамическая фильтрация списка\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"Отфильтруйте список целых чисел 1, 10 и 100 до 10.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"Приручение крипера\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"Покажите уровень здоровья крипера на дисплейной панели.\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"Звук шагов по губке\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"Воспроизведите звук шагов по мокрой губке.\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"Рекурсивная рекурсия\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"Создайте бесконечный список и отобразите его на дисплейной панели.\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"Инструменты для обсидиана?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"Проверьте, может ли каменная кирка сломать блок обсидиана, и покажите результат на дисплейной панели.\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"Умная нажимная плита\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"Создайте систему, которая автоматически открывает дверь, когда вы проходите мимо считывателя сущностей, основываясь на совпадении имени сущности, используя записывающее устройство красного камня, направленное прямо на дверь. (Подсказка: передайте имя сущности как первый аргумент в оператор сравнения).\",\n    \"valuetype.integrateddynamics.error.value\": \"Недопустимое значение\",\n    \"valuetype.integrateddynamics.value_type\": \"Тип значения\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§eТип: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§oЗначение: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§eСигнатура: §r\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"Входные данные \\\"%s\\\" не могут быть обработаны для этого типа значения.\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"Этот тип значения требует входного предмета.\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"Элемент %s недопустим: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"Список ожидает тип значения %s, но был предоставлен %s.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"На позиции %s ожидался тип оператора, но был предоставлен %s.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"Операторное значение %s ожидало типы значений (%s), но было предоставлено (%s).\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"Вставленный предмет не является блоком.\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"Вставленный предмет не содержит жидкости.\",\n    \"valuetype.integrateddynamics.error.no_light_calculator\": \"Тип значения %s не может быть использован для вычисления уровня света.\",\n    \"valuetype.integrateddynamics.error.deserialize\": \"Что-то пошло не так при десериализации '%s': %s\",\n    \"valuetype.integrateddynamics.any\": \"Любой\",\n    \"valuetype.integrateddynamics.any.info\": \"Принимает любые типы значений.\",\n    \"valuetype.integrateddynamics.number\": \"Число\",\n    \"valuetype.integrateddynamics.number.info\": \"Принимает все типы чисел.\",\n    \"valuetype.integrateddynamics.named\": \"Именованный\",\n    \"valuetype.integrateddynamics.named.info\": \"Что-то, что имеет имя, например, отображаемые имена предметов и блоков.\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"Уникально именованный\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"Что-то, что имеет уникальное имя, например, предметы и блоки.\",\n    \"operator.integrateddynamics.string.string_error\": \"Ошибка\",\n    \"operator.integrateddynamics.string.string_error.info\": \"Вызвать пользовательскую ошибку\",\n    \"valuetype.integrateddynamics.boolean\": \"Логическое значение\",\n    \"valuetype.integrateddynamics.integer\": \"Целое число\",\n    \"valuetype.integrateddynamics.double\": \"Двойной\",\n    \"valuetype.integrateddynamics.long\": \"Длинное целое\",\n    \"valuetype.integrateddynamics.string\": \"Строка\",\n    \"valuetype.integrateddynamics.operator\": \"Оператор\",\n    \"valuetype.integrateddynamics.list\": \"Список\",\n    \"valuetype.integrateddynamics.block\": \"Блок\",\n    \"valuetype.integrateddynamics.itemstack\": \"Предмет\",\n    \"valuetype.integrateddynamics.entity\": \"Сущность\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Жидкость\",\n    \"valuetype.integrateddynamics.ingredients\": \"Ингредиенты\",\n    \"valuetype.integrateddynamics.recipe\": \"Рецепт\",\n    \"valuetype.integrateddynamics.ingredients.slot.info\": \"Shift + клик для настройки\",\n    \"parttype.integrateddynamics.error.low_energy\": \"Недостаточно энергии\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"Нет энергии\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"Неактивно\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"Ошибки:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"Аспект: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"Значение: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"Группа: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"ВНИМАНИЕ: Нет доступных аспектов. Это может стать доступным при установке других модов.\",\n    \"parttype.integrateddynamics.tooltip.maxoffset\": \"Максимальное смещение: %s\",\n    \"parttype.integrateddynamics.audio_reader\": \"Считыватель аудио\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"Считывает аудиоинформацию\",\n    \"parttype.integrateddynamics.block_reader\": \"Считыватель блоков\",\n    \"parttype.integrateddynamics.block_reader.info\": \"Считывает информацию о блоках\",\n    \"parttype.integrateddynamics.entity_reader\": \"Считыватель сущностей\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"Считывает информацию о сущностях\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"Межразмерный считыватель\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"Считывает информацию, общую для всех измерений\",\n    \"parttype.integrateddynamics.fluid_reader\": \"Считыватель жидкостей\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"Считывает информацию о резервуарах с жидкостями\",\n    \"parttype.integrateddynamics.inventory_reader\": \"Считыватель инвентаря\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"Считывает данные из инвентарей\",\n    \"parttype.integrateddynamics.network_reader\": \"Считыватель сети\",\n    \"parttype.integrateddynamics.network_reader.info\": \"Считывает свойства целевой сети\",\n    \"parttype.integrateddynamics.machine_reader\": \"Считыватель машин\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"Считывает свойства машин\",\n    \"parttype.integrateddynamics.redstone_reader\": \"Считыватель красного камня\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"Считывает значения красного камня\",\n    \"parttype.integrateddynamics.world_reader\": \"Считыватель мира\",\n    \"parttype.integrateddynamics.world_reader.info\": \"Считывает информацию о мире, блоках и сущностях\",\n    \"parttype.integrateddynamics.audio_writer\": \"Звуковой записыватель\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"Воспроизводит звуки и музыку\",\n    \"parttype.integrateddynamics.effect_writer\": \"Записыватель эффектов\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"Выводит частицы\",\n    \"parttype.integrateddynamics.entity_writer\": \"Записыватель сущностей\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"Записывает конфигурацию сущностей\",\n    \"parttype.integrateddynamics.machine_writer\": \"Записыватель машин\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"Записывает конфигурацию машин\",\n    \"parttype.integrateddynamics.inventory_writer\": \"Записыватель инвентаря\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"Записывает данные в инвентарь\",\n    \"parttype.integrateddynamics.redstone_writer\": \"Записыватель красного камня\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"Записывает данные в красный камень\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"Эта панель не принимает %s.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"Статическая световая панель\",\n    \"parttype.integrateddynamics.static_light_panel.info\": \"Простая световая панель, которая всегда активна\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"Динамическая световая панель\",\n    \"parttype.integrateddynamics.display_panel\": \"Дисплейная панель\",\n    \"parttype.integrateddynamics.display_panel.info\": \"Визуально отображает значение переменной в мире как текст\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"Моно-направленный коннектор\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"Может соединяться с другими моно-направленными коннекторами на расстоянии по прямой линии\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"Омни-направленный коннектор\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"Может соединяться с другими омни-направленными коннекторами в любом месте, включая разные измерения.\",\n    \"variable.integrateddynamics.error.invalid_item\": \"Текущая переменная не может быть использована в этом аспекте.\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"Часть с ID %s не найдена в текущей сети.\",\n    \"variable.integrateddynamics.error.recursion\": \"Переменная с ID %s ссылается сама на себя.\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§oID прокси: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"Прокси с ID %s не найден в текущей сети.\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"Прокси с ID %s не предоставляет переменную.\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"Прокси с ID %s должен предоставлять переменную типа %s, но обнаружен тип %s.\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§oID задержки: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"Задержка с ID %s не найдена в текущей сети.\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"Задержка с ID %s не предоставляет переменную.\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"Задержка с ID %s должна предоставлять переменную типа %s, но обнаружен тип %s.\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§eАспект: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§eТип: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§oID части: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"Этот аспект ожидает %s, но был предоставлен %s.\",\n    \"aspect.integrateddynamics.error.recursion\": \"Переменная аспекта для %s ссылается сама на себя.\",\n    \"aspect.integrateddynamics.error.no_value_interface\": \"Не найден корректный интерфейс значений.\",\n    \"aspect.integrateddynamics.error.no_value_interface_value\": \"Не найдено корректное значение интерфейса значений.\",\n    \"aspect.integrateddynamics.name\": \"Аспект\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp\": \"Нота арфы\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.harp.info\": \"Считывает ноту арфы, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum\": \"Нота бас-барабана\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.basedrum.info\": \"Считывает ноту бас-барабана, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"Нота малого барабана\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"Считывает ноту малого барабана, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat\": \"Нота тарелки\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.hat.info\": \"Считывает ноту тарелки, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass\": \"Нота баса\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bass.info\": \"Считывает ноту баса, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute\": \"Нота флейты\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.flute.info\": \"Считывает ноту флейты, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell\": \"Нота колокольчика\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bell.info\": \"Считывает ноту колокольчика, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar\": \"Нота гитары\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.guitar.info\": \"Считывает ноту гитары, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime\": \"Нота колокольчиков\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.chime.info\": \"Считывает ноту колокольчиков, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone\": \"Нота ксилофона\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.xylophone.info\": \"Считывает ноту ксилофона, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone\": \"Нота железного ксилофона\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.iron_xylophone.info\": \"Считывает ноту железного ксилофона, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell\": \"Нота коровьего колокольчика\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.cow_bell.info\": \"Считывает ноту коровьего колокольчика, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo\": \"Нота диджериду\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.didgeridoo.info\": \"Считывает ноту диджериду, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit\": \"Нота бита\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.bit.info\": \"Считывает ноту бита, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo\": \"Нота банджо\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.banjo.info\": \"Считывает ноту банджо, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling\": \"Нота плинга\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.pling.info\": \"Считывает ноту плинга, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp\": \"Нота арфы\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.harp.info\": \"Выводит ноту арфы, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum\": \"Нота бас-барабана\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.basedrum.info\": \"Выводит ноту бас-барабана, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"Нота малого барабана\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"Выводит ноту малого барабана, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat\": \"Нота тарелки\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.hat.info\": \"Выводит ноту тарелки, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass\": \"Нота баса\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bass.info\": \"Выводит ноту баса, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute\": \"Нота флейты\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.flute.info\": \"Выводит ноту флейты, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell\": \"Нота колокольчика\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bell.info\": \"Выводит ноту колокольчика, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar\": \"Нота гитары\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.guitar.info\": \"Выводит ноту гитары, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime\": \"Нота колокольчиков\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.chime.info\": \"Выводит ноту колокольчиков, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone\": \"Нота ксилофона\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.xylophone.info\": \"Выводит ноту ксилофона, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone\": \"Нота железного ксилофона\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.iron_xylophone.info\": \"Выводит ноту железного ксилофона, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell\": \"Нота коровьего колокольчика\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.cow_bell.info\": \"Выводит ноту коровьего колокольчика, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo\": \"Нота диджериду\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.didgeridoo.info\": \"Выводит ноту диджериду, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit\": \"Нота бита\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.bit.info\": \"Выводит ноту бита, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo\": \"Нота банджо\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.banjo.info\": \"Выводит ноту банджо, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling\": \"Нота плинга\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.pling.info\": \"Выводит ноту плинга, ожидаемый диапазон [0, 24]\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"Звук\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"Воспроизводит указанный звук по имени\",\n    \"aspect.integrateddynamics.write.string.audio.text\": \"Текст\",\n    \"aspect.integrateddynamics.write.string.audio.text.info\": \"Преобразует указанный текст в речь\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_angry_villager\": \"Частица: Злой житель\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble\": \"Частица: Пузырь\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble.info\": \"Испускает пузырь с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud\": \"Частица: Облако\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_cloud.info\": \"Испускает облако с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit\": \"Частица: Критический удар\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crit.info\": \"Испускает частицу критического удара с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_damage_indicator\": \"Частица: Индикатор урона\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath\": \"Частица: Дыхание дракона\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dragon_breath.info\": \"Испускает частицу дыхания дракона с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_lava\": \"Частица: Капающая лава\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava\": \"Частица: Падающая лава\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_lava.info\": \"Испускает падающую лаву с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_lava\": \"Частица: Приземляющаяся лава\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_water\": \"Частица: Капающая вода\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water\": \"Частица: Падающая вода\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_water.info\": \"Испускает падающую воду с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect\": \"Частица: Эффект\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_effect.info\": \"Испускает частицу эффекта с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_elder_guardian\": \"Частица: Древний страж\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchanted_hit\": \"Частица: Магический удар\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant\": \"Частица: Зачарование\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_enchant.info\": \"Испускает частицу зачарования с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod\": \"Частица: Концевой стержень\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_end_rod.info\": \"Испускает частицу концевого стержня с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter\": \"Частица: Источник взрыва\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion_emitter.info\": \"Испускает частицу источника взрыва с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_explosion\": \"Частица: Взрыв\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sonic_boom\": \"Частица: Звуковой бум\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework\": \"Частица: Фейерверк\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_firework.info\": \"Испускает частицу фейерверка с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_fishing\": \"Частица: Рыбалка\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame\": \"Частица: Пламя\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flame.info\": \"Испускает частицу пламени с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame\": \"Частица: Пламя душ\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul_fire_flame.info\": \"Испускает частицу пламени душ с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul\": \"Частица: Душа скалка\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_soul.info\": \"Испускает частицу души скалка с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop\": \"Частица: Вспышка заряда скалка\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sculk_charge_pop.info\": \"Испускает вспышку заряда скалка с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul\": \"Частица: Душа\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_soul.info\": \"Испускает частицу души с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash\": \"Частица: Вспышка\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_flash.info\": \"Испускает вспышку с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_happy_villager\": \"Частица: Счастливый житель\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter\": \"Частица: Компостер\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_composter.info\": \"Испускает частицу компостера с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart\": \"Частица: Сердце\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_heart.info\": \"Испускает частицу сердца с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect\": \"Частица: Мгновенный эффект\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_instant_effect.info\": \"Испускает частицу мгновенного эффекта с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime\": \"Частица: Слизь\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_slime.info\": \"Испускает частицу слизи с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball\": \"Частица: Снежок\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_item_snowball.info\": \"Испускает частицу снежка с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke\": \"Частица: Большой дым\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_large_smoke.info\": \"Испускает частицу большого дыма с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava\": \"Частица: Лава\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_lava.info\": \"Испускает частицу лавы с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium\": \"Частица: Мицелий\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_mycelium.info\": \"Испускает частицу мицелия с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note\": \"Частица: Нота\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_note.info\": \"Испускает частицу ноты с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof\": \"Частица: Пфф\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_poof.info\": \"Испускает частицу пфф с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal\": \"Частица: Портал\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_portal.info\": \"Испускает частицу портала с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain\": \"Частица: Дождь\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_rain.info\": \"Испускает частицу дождя с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke\": \"Частица: Дым\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_smoke.info\": \"Испускает частицу дыма с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze\": \"Частица: Чихание\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sneeze.info\": \"Испускает частицу чихания с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit\": \"Частица: Плевок\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spit.info\": \"Испускает частицу плевка с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink\": \"Частица: Чернила кальмара\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_squid_ink.info\": \"Испускает частицу чернил кальмара с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack\": \"Частица: Взмах атаки\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_sweep_attack.info\": \"Испускает частицу взмаха атаки с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying\": \"Частица: Тотем бессмертия\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_totem_of_undying.info\": \"Испускает частицу тотема бессмертия с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_underwater\": \"Частица: Под водой\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash\": \"Частица: Брызги\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_splash.info\": \"Испускает частицу брызг с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch\": \"Частица: Ведьма\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_witch.info\": \"Испускает частицу ведьмы с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop\": \"Частица: Лопающийся пузырь\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_pop.info\": \"Испускает частицу лопающегося пузыря с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down\": \"Частица: Нисходящий поток\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_current_down.info\": \"Испускает частицу нисходящего потока с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up\": \"Частица: Восходящий поток\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_bubble_column_up.info\": \"Испускает частицу восходящего потока с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus\": \"Частица: Наутилус\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_nautilus.info\": \"Испускает частицу наутилуса с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin\": \"Частица: Дельфин\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dolphin.info\": \"Испускает частицу дельфина с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke\": \"Частица: Уютный дым костра\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_cosy_smoke.info\": \"Испускает частицу уютного дыма костра с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke\": \"Частица: Сигнальный дым костра\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_campfire_signal_smoke.info\": \"Испускает частицу сигнального дыма костра с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey\": \"Частица: Капающий мед\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_honey.info\": \"Испускает частицу капающего меда с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey\": \"Частица: Падающий мед\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_honey.info\": \"Испускает частицу падающего меда с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey\": \"Частица: Приземляющийся мед\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_honey.info\": \"Испускает частицу приземляющегося меда с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar\": \"Частица: Падающий нектар\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_nectar.info\": \"Испускает частицу падающего нектара с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom\": \"Частица: Падающий споровый цвет\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_spore_blossom.info\": \"Испускает частицу падающего спорового цвета с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash\": \"Частица: Пепел\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_ash.info\": \"Испускает частицу пепла с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore\": \"Частица: Багровая спора\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_crimson_spore.info\": \"Испускает частицу багровой споры с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore\": \"Частица: Искаженная спора\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_warped_spore.info\": \"Испускает частицу искаженной споры с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air\": \"Частица: Воздушный споровый цвет\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_spore_blossom_air.info\": \"Испускает частицу воздушного спорового цвета с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear\": \"Частица: Капающая обсидиановая слеза\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_obsidian_tear.info\": \"Испускает частицу капающей обсидиановой слезы с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear\": \"Частица: Падающая обсидиановая слеза\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_obsidian_tear.info\": \"Испускает частицу падающей обсидиановой слезы с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear\": \"Частица: Приземляющаяся обсидиановая слеза\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_landing_obsidian_tear.info\": \"Испускает частицу приземляющейся обсидиановой слезы с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal\": \"Частица: Обратный портал\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_reverse_portal.info\": \"Испускает частицу обратного портала с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash\": \"Частица: Белый пепел\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_white_ash.info\": \"Испускает частицу белого пепла с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame\": \"Частица: Маленькое пламя\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_small_flame.info\": \"Испускает частицу маленького пламени с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake\": \"Частица: Снежинка\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_snowflake.info\": \"Испускает частицу снежинки с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava\": \"Частица: Капающая лава сталактита\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_lava.info\": \"Испускает частицу капающей лавы сталактита с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava\": \"Частица: Падающая лава сталактита\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_lava.info\": \"Испускает частицу падающей лавы сталактита с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water\": \"Частица: Капающая вода сталактита\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_dripping_dripstone_water.info\": \"Испускает частицу капающей воды сталактита с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water\": \"Частица: Падающая вода сталактита\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_falling_dripstone_water.info\": \"Испускает частицу падающей воды сталактита с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink\": \"Частица: Светящиеся чернила кальмара\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow_squid_ink.info\": \"Испускает частицу светящихся чернил кальмара с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow\": \"Частица: Свечение\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_glow.info\": \"Испускает частицу свечения с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on\": \"Частица: Нанесение воска\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_on.info\": \"Испускает частицу нанесения воска с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off\": \"Частица: Удаление воска\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_wax_off.info\": \"Испускает частицу удаления воска с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark\": \"Частица: Электрическая искра\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_electric_spark.info\": \"Испускает частицу электрической искры с определенной скоростью.\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape\": \"Частица: Скребок\",\n    \"aspect.integrateddynamics.write.double.effect.particle.minecraft_scrape.info\": \"Испускает частицу скребка с определенной скоростью.\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"Наличие блока\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"Если в целевом пространстве есть блок\",\n    \"aspect.integrateddynamics.read.string.block.dimension\": \"Измерение\",\n    \"aspect.integrateddynamics.read.string.block.dimension.info\": \"ID измерения целевого мира\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"Координата X\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"Координата X цели\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Координата Y\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"Координата Y цели\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Координата Z\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"Координата Z цели\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"NBT сущности плитки\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"Целевая сущность плитки в формате NBT\",\n    \"aspect.integrateddynamics.read.string.block.biome\": \"Биом\",\n    \"aspect.integrateddynamics.read.string.block.biome.info\": \"Название биома в цели\",\n    \"aspect.integrateddynamics.read.integer.block.light\": \"Уровень света\",\n    \"aspect.integrateddynamics.read.integer.block.light.info\": \"Уровень света в цели\",\n    \"aspect.integrateddynamics.read.block.block\": \"Блок\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"Целевой блок\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"Вращение рамки предмета\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"Индекс вращения предмета в прикрепленной рамке предметов.\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"Сущности\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"Сущности в целевом пространстве\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"Игроки\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"Игроки, находящиеся в целевом пространстве\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"Сущность\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"Выбранная сущность в целевом пространстве\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"Рамка предмета\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"Предмет в рамке предметов, прикрепленной к считывателю\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"Случайное число\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"Случайное целочисленное значение\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"Количество игроков\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"Количество игроков на сервере\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"Время тика\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"Среднее время тика в наносекундах для сервера\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"Среднее количество тиков в секунду по всем мирам\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"Игроки\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"Игроки, находящиеся онлайн на этом сервере\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"Бак пуст\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"Если целевой резервуар с жидкостью пуст\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"Бак полон\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"Если целевой резервуар с жидкостью полон\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"Бак не пуст\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"Если в целевом резервуаре с жидкостью что-то есть\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"Это бак\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"Если цель имеет резервуар для жидкости\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"Резервуары\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"Количество резервуаров, найденных в цели\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"Коэффициент заполнения резервуара\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"Количество жидкости в активном резервуаре, деленное на его вместимость\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"Жидкости в резервуарах\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"Список жидкостей во всех резервуарах\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"Вместимости резервуаров\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"Список вместимостей всех резервуаров\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"Жидкость в резервуаре\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"Жидкость в целевом резервуаре\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"Жидкость в блоке\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"Жидкость в целевом блоке\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"Инвентарь полон\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"Если все слоты содержат хотя бы один предмет\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"Инвентарь пуст\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"Если все слоты пусты\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"Инвентарь не пуст\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"Если есть хотя бы один предмет\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"Это инвентарь\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"Если цель имеет инвентарь\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"Количество в инвентаре\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"Получить общее количество предметов\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"Размер стека в слоте\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"Получить размер стека в активном слоте\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"Слоты\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"Количество слотов в инвентаре\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"Заполненные слоты\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"Количество слотов, содержащих предмет\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"Коэффициент заполнения\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"Количество непустых слотов, деленное на общее количество слотов\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"Предметы\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"Получить список предметов в инвентаре\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"Предмет в слоте\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"Получить предмет в активном слоте\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"Это рабочая машина\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"Если цель является рабочей машиной\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"Есть работа\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"Если у цели есть работа для обработки\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"Может работать\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"Если цель может начать работу в текущем состоянии\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"В процессе работы\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"Если цель в данный момент работает\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"Имеет температуру\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"Если у цели есть определенная температура\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"Температура\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature.info\": \"Текущая температура цели в градусах Кельвина\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"Максимальная температура\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature.info\": \"Максимальная температура цели в градусах Кельвина\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"Минимальная температура\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature.info\": \"Минимальная температура цели в градусах Кельвина\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"Базовая температура\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature.info\": \"Базовая температура цели в градусах Кельвина\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"Обработчик рецептов\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"Если цель может обрабатывать рецепты\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"Рецепты\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"Рецепты, которые могут быть обработаны целью\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"Выход рецепта по входу\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"Оператор, который возвращает выходной результат рецепта для заданного входа.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"Входы рецепта по выходу\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"Оператор, который возвращает все возможные входные данные рецепта для заданного выхода.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"Рецепты по входу\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"Оператор, который возвращает все возможные рецепты для заданного входа.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"Рецепты по выходу\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"Оператор, который возвращает все возможные рецепты для заданного выхода.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"Рецепт по входу\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"Оператор, который возвращает рецепт для заданного входа.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"Рецепт по выходу\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"Оператор, который возвращает рецепт для заданного выхода.\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"Это сеть\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"Если цель имеет сеть\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"Элементы\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"Количество элементов в целевой сети\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"Энергетические батареи\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"Количество энергетических батарей в целевой сети\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"Скорость потребления энергии\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"Текущая скорость потребления энергии всеми частями ID в целевой сети за тик\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"Значение\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"Значение, которое предоставляется целью\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"Максимальный уровень красного камня\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"Если уровень сигнала красного камня находится на максимуме\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"Нулевой уровень красного камня\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"Если сигнала красного камня нет\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"Уровень красного камня\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"Если есть сигнал красного камня\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"Часы красного камня\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"Импульсы с настраиваемой длиной и интервалом.\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"Красный камень\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"Получить точный уровень сигнала красного камня\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"Компаратор\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"Получить значение компаратора\",\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"Красный камень\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"Выводит полный сигнал красного камня, если значение истинно\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"Импульс красного камня\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"Выводит импульс с настроенным значением красного камня, если заданное значение изменяется.\",\n    \"aspect.integrateddynamics.write.integer.redstone\": \"Красный камень\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"Выводит значение как точный сигнал красного камня\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"Импульс красного камня\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"Выводит импульс с настроенным значением красного камня, если заданное значение изменяется.\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"Есть сущность\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"Если в целевом пространстве есть сущность\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"Есть моб\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"Если в целевом пространстве есть моб\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"Есть животное\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"Если в целевом пространстве есть животное\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"Есть игрок\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"Если в целевом пространстве есть игрок\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"Есть предмет\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"Если в целевом пространстве есть предмет\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"Погода: Ясно\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"Если в мире ясная погода\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"Погода: Дождь\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining.info\": \"Если в мире идет дождь\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"Погода: Гроза\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"Если в мире гроза\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"День\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"Если в целевом мире сейчас день\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"Ночь\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"Если в целевом мире сейчас ночь\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"Сущности\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"Количество сущностей в целевом пространстве\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"Обратный отсчет до дождя\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"Количество тиков до начала дождя в этом мире\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"Время тика\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"Среднее время тика в миллисекундах для этого мира\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"Время дня\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"Время, прошедшее в течение дня в целевом мире\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"Уровень света\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"Уровень света в цели\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"Количество игроков\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"Игроки, находящиеся онлайн в этом мире\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"Количество тиков в секунду в этом мире\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"Время\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"Время мира, зависящее от команды установки времени\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"Общее время\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"Время мира, независимое от команды установки времени\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"Имя мира\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"Имя целевого мира\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"Игроки\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"Игроки, находящиеся в этом мире\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"ID резервуара\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"ID слота\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"Элемент списка\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"Диапазон\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"Канал\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"Интервал импульса\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"Длина импульса\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"Временное смещение импульса\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"Громкость\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"Частота\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"Смещение по X\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Смещение по Y\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Смещение по Z\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"Количество частиц\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"Разброс по X\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Разброс по Y\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Разброс по Z\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"Сильный сигнал\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"Если должен быть симулирован факел красного камня вместо обычной пыли красного камня\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"Значение импульса\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"Значение импульса, которое должно быть отправлено.\",\n    \"operator.integrateddynamics\": \"Оператор\",\n    \"operator.integrateddynamics.applied.operator_name\": \"Применён %s [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"§Применён к: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§eОператор: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§eКатегория: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§eID переменных: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§eТип входных данных %s: §r\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§eТип выходных данных: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"Оператор %s получил входные данные длиной %s, тогда как требуется длина %s.\",\n    \"operator.integrateddynamics.error.operator_param_wrong_input_length\": \"Требуется длина входных данных оператора %s, тогда как %s имеет длину входных данных только %s.\",\n    \"operator.integrateddynamics.error.null_type\": \"Оператор %s получил входные данные с пустой переменной в позиции %s.\",\n    \"operator.integrateddynamics.error.wrong_type\": \"Оператор %s получил входные данные типа %s в позиции %s, тогда как ожидался тип %s.\",\n    \"operator.integrateddynamics.error.wrong_type_output\": \"Вычисление для оператора %s вернуло %s вместо ожидаемого %s.\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"Оператор %s получил входные данные типа %s в позиции %s, тогда как ожидался тип %s для карринга.\",\n    \"operator.integrateddynamics.error.currying_overflow\": \"При выполнении карринга оператора %s (%s входов) с %s входами ожидался оператор в качестве выходных данных, но получен %s.\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"Оператор %s получил входной оператор, который возвращает тип %s, тогда как ожидался тип %s.\",\n    \"operator.integrateddynamics.error.illegal_property\": \"Свойство должно возвращать %s, но для оператора %s найдено %s.\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"Переменная с ID %s не найдена в текущей сети.\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"Переменная с ID %s зависит сама от себя, бесконечная рекурсия.\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"Оператор %s получил оператор %s с длиной входных данных %s, тогда как требуется длина %s.\",\n    \"operator.integrateddynamics.error.operator_recursion_limit\": \"Достигнут лимит рекурсии оператора %s вызовов для %s. Пытаетесь сломать игру?\",\n    \"operator.integrateddynamics.error.operator_nbt_path_expression\": \"Найдено некорректное выражение пути NBT '%s': %s\",\n    \"operator.integrateddynamics.error.cast.no_mapping\": \"Отсутствует сопоставление приведения типов из %s в %s.\",\n    \"operator.integrateddynamics.error.cast.illegal\": \"Попытка приведения %s к %s для значения \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.cast.unexpected\": \"Значение типа %s не соответствует ожидаемому типу %s для приведения к %s.\",\n    \"operator.integrateddynamics.error.divide_by_zero\": \"Деление на ноль недопустимо.\",\n    \"operator.integrateddynamics.error.parse\": \"Значение \\\"%s\\\" не может быть преобразовано в %s.\",\n    \"operator.integrateddynamics.error.no_derserializer\": \"Не найден сериализатор для десериализации значения оператора \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.regex.invalid\": \"Неверное регулярное выражение: \\\"%s\\\"\",\n    \"operator.integrateddynamics.error.substring.index_negative\": \"Индексы подстроки не должны быть отрицательными.\",\n    \"operator.integrateddynamics.error.substring.longer_than_string\": \"Индексы подстроки не должны превышать длину строки.\",\n    \"operator.integrateddynamics.error.group.index_negative\": \"Индекс группы не должен быть отрицательным.\",\n    \"operator.integrateddynamics.error.group.no_match\": \"Совпадений по группе регулярного выражения \\\"%s\\\" не найдено в строке \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.group.no_match_group\": \"Совпадений по группе регулярного выражения \\\"%s\\\" не найдено в строке \\\"%s\\\" для группы \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.regex_scan.index_negative\": \"Индекс сканирования регулярного выражения не должен быть отрицательным.\",\n    \"operator.integrateddynamics.error.regex_scan.no_match_group\": \"Совпадений по сканированию регулярного выражения \\\"%s\\\" не найдено в строке \\\"%s\\\" для группы \\\"%s\\\".\",\n    \"operator.integrateddynamics.error.replace_regex.invalid_group\": \"Неверная группа в замене регулярного выражения для шаблона \\\"%s\\\": %s\",\n    \"operator.integrateddynamics.error.infinite_list.illegal\": \"Оператор %s не может быть применён к бесконечным спискам.\",\n    \"operator.integrateddynamics.error.index_out_of_bounds\": \"Индекс выходит за пределы. Попытка получить элемент %s из списка длиной %s. Используйте оператор getOrDefault, чтобы предоставить значение по умолчанию при выходе индекса за пределы.\",\n    \"operator.integrateddynamics.error.slice.index_negative\": \"Индексы среза не должны быть отрицательными.\",\n    \"operator.integrateddynamics.error.reduce.empty\": \"Оператор reduce1 попытался получить первый элемент пустого списка. Вместо этого используйте оператор reduce, чтобы предоставить базовое значение для поддержки пустых списков.\",\n    \"operator.integrateddynamics.error.operator_not_found\": \"Не удалось найти оператор с именем \\\"%s\\\".\",\n    \"operator.integrateddynamics.logical\": \"Логический\",\n    \"operator.integrateddynamics.logical.basename\": \"Логический %s\",\n    \"operator.integrateddynamics.logical.and\": \"И\",\n    \"operator.integrateddynamics.logical.or\": \"Или\",\n    \"operator.integrateddynamics.logical.not\": \"Не\",\n    \"operator.integrateddynamics.logical.nand\": \"Не-И\",\n    \"operator.integrateddynamics.logical.nor\": \"Не-Или\",\n    \"operator.integrateddynamics.arithmetic\": \"Арифметический\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"Арифметический %s\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"Сложение\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"Вычитание\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"Умножение\",\n    \"operator.integrateddynamics.arithmetic.division\": \"Деление\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"Максимум\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"Возвращает наибольшее из двух значений.\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"Минимум\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"Возвращает наименьшее из двух значений.\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"Остаток от деления\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"Инкремент\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"Декремент\",\n    \"operator.integrateddynamics.integer\": \"Целочисленный\",\n    \"operator.integrateddynamics.integer.basename\": \"Целочисленный %s\",\n    \"operator.integrateddynamics.integer.modulus\": \"Остаток от деления\",\n    \"operator.integrateddynamics.integer.increment\": \"Инкремент\",\n    \"operator.integrateddynamics.integer.decrement\": \"Декремент\",\n    \"operator.integrateddynamics.relational\": \"Отношения\",\n    \"operator.integrateddynamics.relational.basename\": \"Отношение %s\",\n    \"operator.integrateddynamics.relational.equals\": \"Равно\",\n    \"operator.integrateddynamics.relational.gt\": \"Больше\",\n    \"operator.integrateddynamics.relational.lt\": \"Меньше\",\n    \"operator.integrateddynamics.relational.notequals\": \"Не равно\",\n    \"operator.integrateddynamics.relational.ge\": \"Больше или равно\",\n    \"operator.integrateddynamics.relational.le\": \"Меньше или равно\",\n    \"operator.integrateddynamics.binary\": \"Бинарный\",\n    \"operator.integrateddynamics.binary.basename\": \"Бинарный %s\",\n    \"operator.integrateddynamics.binary.and\": \"И\",\n    \"operator.integrateddynamics.binary.or\": \"Или\",\n    \"operator.integrateddynamics.binary.xor\": \"Исключающее Или\",\n    \"operator.integrateddynamics.binary.complement\": \"Дополнение\",\n    \"operator.integrateddynamics.binary.rshift\": \"Сдвиг вправо\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"Сдвиг вправо с заполнением единицами\",\n    \"operator.integrateddynamics.binary.lshift\": \"Сдвиг влево\",\n    \"operator.integrateddynamics.binary.rzshift\": \"Сдвиг вправо с нулями\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"Сдвиг вправо с заполнением нулями\",\n    \"operator.integrateddynamics.string\": \"Строка\",\n    \"operator.integrateddynamics.string.basename\": \"Строка %s\",\n    \"operator.integrateddynamics.string.length\": \"Длина\",\n    \"operator.integrateddynamics.string.length.info\": \"Длина заданной строки\",\n    \"operator.integrateddynamics.string.concat\": \"Конкатенация\",\n    \"operator.integrateddynamics.string.concat.info\": \"Объединить две заданные строки в одну\",\n    \"operator.integrateddynamics.string.contains\": \"Содержит подстроку\",\n    \"operator.integrateddynamics.string.contains.info\": \"Если заданная подстрока содержится в заданной строке.\",\n    \"operator.integrateddynamics.string.contains_regex\": \"Содержит регулярное выражение\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"Если заданное регулярное выражение соответствует любой подстроке заданной строки.\",\n    \"operator.integrateddynamics.string.matches_regex\": \"Соответствует регулярному выражению\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"Если заданное регулярное выражение соответствует заданной строке.\",\n    \"operator.integrateddynamics.string.index_of\": \"Индекс первого вхождения\",\n    \"operator.integrateddynamics.string.index_of.info\": \"Получить индекс первого вхождения подстроки, соответствующей поисковому запросу, в заданной строке.\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"Индекс по регулярному выражению\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"Получить индекс первого вхождения подстроки, соответствующей шаблону, в заданной строке.\",\n    \"operator.integrateddynamics.string.starts_with\": \"Начинается с\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"Если заданная подстрока совпадает с началом заданной строки.\",\n    \"operator.integrateddynamics.string.ends_with\": \"Заканчивается на\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"Если заданная строка совпадает с концом заданной строки.\",\n    \"operator.integrateddynamics.string.split_on\": \"Разделить по\",\n    \"operator.integrateddynamics.string.split_on.info\": \"Получить список частей строки, разделённых по заданному (буквальному) разделителю.\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"Разделить по регулярному выражению\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"Получить список частей строки, разделённых по заданному регулярному выражению.\",\n    \"operator.integrateddynamics.string.substring\": \"Подстрока\",\n    \"operator.integrateddynamics.string.substring.info\": \"Получить подстроку из заданной строки, начиная с указанного индекса (включительно) до указанного индекса (исключительно).\",\n    \"operator.integrateddynamics.string.regex_group\": \"Группа регулярного выражения\",\n    \"operator.integrateddynamics.string.regex_group.info\": \"Найти совпадение с заданным регулярным выражением и вернуть группу с указанным индексом в найденном совпадении.\",\n    \"operator.integrateddynamics.string.regex_groups\": \"Группы регулярного выражения\",\n    \"operator.integrateddynamics.string.regex_groups.info\": \"Найти совпадение с заданным регулярным выражением и вернуть список всех групп этого совпадения.\",\n    \"operator.integrateddynamics.string.regex_scan\": \"Сканирование регулярным выражением\",\n    \"operator.integrateddynamics.string.regex_scan.info\": \"Найти все совпадения с заданным регулярным выражением и вернуть группу с указанным индексом из каждого совпадения.\",\n    \"operator.integrateddynamics.string.replace\": \"Заменить\",\n    \"operator.integrateddynamics.string.replace.info\": \"Найти все вхождения поискового запроса и заменить их на заданную строку.\",\n    \"operator.integrateddynamics.string.replace_regex\": \"Заменить по регулярному выражению\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"Найти все совпадения с шаблоном поиска и заменить их на заданную строку.\",\n    \"operator.integrateddynamics.string.join\": \"Объединить\",\n    \"operator.integrateddynamics.string.join.info\": \"Создать объединённую строку с использованием заданного разделителя и списка строк.\",\n    \"operator.integrateddynamics.string.name\": \"Преобразовать в строку\",\n    \"operator.integrateddynamics.string.name.info\": \"Получить строковое представление или имя именованного объекта или преобразовать его в строку.\",\n    \"operator.integrateddynamics.string.unique_name\": \"Уникальное имя\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"Получить уникальное имя объекта.\",\n    \"operator.integrateddynamics.double\": \"Число с плавающей точкой\",\n    \"operator.integrateddynamics.double.basename\": \"Число с плавающей точкой %s\",\n    \"operator.integrateddynamics.number\": \"Число\",\n    \"operator.integrateddynamics.number.basename\": \"Число %s\",\n    \"operator.integrateddynamics.number.round\": \"Округление\",\n    \"operator.integrateddynamics.number.round.info\": \"Округлить до ближайшего целого числа\",\n    \"operator.integrateddynamics.number.ceil\": \"Округление вверх\",\n    \"operator.integrateddynamics.number.ceil.info\": \"Округлить вверх до ближайшего целого числа\",\n    \"operator.integrateddynamics.number.floor\": \"Округление вниз\",\n    \"operator.integrateddynamics.number.floor.info\": \"Округлить вниз до ближайшего целого числа\",\n    \"operator.integrateddynamics.number.compact\": \"Компактный вид\",\n    \"operator.integrateddynamics.number.compact.info\": \"Показать более короткое, менее точное представление числа\",\n    \"operator.integrateddynamics.list\": \"Список\",\n    \"operator.integrateddynamics.list.basename\": \"Список %s\",\n    \"operator.integrateddynamics.list.length\": \"Длина\",\n    \"operator.integrateddynamics.list.length.info\": \"Длина заданного списка\",\n    \"operator.integrateddynamics.list.empty\": \"Пустой\",\n    \"operator.integrateddynamics.list.empty.info\": \"Если список пуст\",\n    \"operator.integrateddynamics.list.notempty\": \"Не пустой\",\n    \"operator.integrateddynamics.list.notempty.info\": \"Если список не пуст\",\n    \"operator.integrateddynamics.list.get\": \"Получить\",\n    \"operator.integrateddynamics.list.get.info\": \"Получить элемент списка по заданной позиции, вызывает ошибку, если индекс выходит за пределы.\",\n    \"operator.integrateddynamics.list.get_or_default\": \"Получить или значение по умолчанию\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"Безопасно получить элемент списка по заданной позиции, если элемент недоступен, вернуть заданное значение по умолчанию.\",\n    \"operator.integrateddynamics.list.contains\": \"Содержит\",\n    \"operator.integrateddynamics.list.contains.info\": \"Если список содержит заданный элемент.\",\n    \"operator.integrateddynamics.list.contains_p\": \"Содержит (по предикату)\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"Если хотя бы один из элементов списка возвращает true для заданного предиката.\",\n    \"operator.integrateddynamics.list.count\": \"Количество\",\n    \"operator.integrateddynamics.list.count.info\": \"Количество раз, когда заданный элемент встречается в списке.\",\n    \"operator.integrateddynamics.list.count_p\": \"Количество (по предикату)\",\n    \"operator.integrateddynamics.list.count_p.info\": \"Количество раз, когда заданный предикат возвращает true для элементов в списке.\",\n    \"operator.integrateddynamics.list.append\": \"Добавить\",\n    \"operator.integrateddynamics.list.append.info\": \"Добавить к заданному списку указанный элемент.\",\n    \"operator.integrateddynamics.list.concat\": \"Объединить\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"Ленивый конструктор списка\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"Создать список лениво, используя начальное значение и оператор, который применяется к предыдущему элементу для получения следующего.\",\n    \"operator.integrateddynamics.list.head\": \"Первый элемент\",\n    \"operator.integrateddynamics.list.head.info\": \"Получить первый элемент заданного списка.\",\n    \"operator.integrateddynamics.list.tail\": \"Хвост\",\n    \"operator.integrateddynamics.list.tail.info\": \"Удалить первый элемент из списка.\",\n    \"operator.integrateddynamics.list.uniq_p\": \"Уникальные (по предикату)\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"Удалить дубликаты всех элементов в списке на основе заданного предиката сравнения.\",\n    \"operator.integrateddynamics.list.uniq\": \"Уникальные\",\n    \"operator.integrateddynamics.list.uniq.info\": \"Удалить дубликаты всех элементов в списке.\",\n    \"operator.integrateddynamics.list.slice\": \"Срез\",\n    \"operator.integrateddynamics.list.slice.info\": \"Получить подмножество заданного списка от указанного индекса (включительно) до указанного индекса (исключительно).\",\n    \"operator.integrateddynamics.list.intersection\": \"Пересечение\",\n    \"operator.integrateddynamics.list.intersection.info\": \"Возвращает список элементов, присутствующих в обоих предоставленных списках.\",\n    \"operator.integrateddynamics.block\": \"Блок\",\n    \"operator.integrateddynamics.block.basename\": \"Блок %s\",\n    \"operator.integrateddynamics.block.opaque\": \"Непрозрачный\",\n    \"operator.integrateddynamics.block.opaque.info\": \"Если заданный блок непрозрачен (не прозрачен)\",\n    \"operator.integrateddynamics.block.itemstack\": \"Предмет\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"Представление предмета для заданного блока\",\n    \"operator.integrateddynamics.block.mod\": \"Мод\",\n    \"operator.integrateddynamics.block.mod.info\": \"Имя мода, которому принадлежит заданный блок\",\n    \"operator.integrateddynamics.block.breaksound\": \"Звук разрушения блока\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"Звук разрушения заданного блока\",\n    \"operator.integrateddynamics.block.placesound\": \"Звук установки блока\",\n    \"operator.integrateddynamics.block.placesound.info\": \"Звук установки заданного блока\",\n    \"operator.integrateddynamics.block.stepsound\": \"Звук шагов по блоку\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"Звук шагов по заданному блоку\",\n    \"operator.integrateddynamics.block.isshearable\": \"Блок поддается стрижке\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"Если заданный блок можно стричь\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"Блок может поддерживать растение-блок\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"Если заданный блок может поддерживать заданное растение-блок\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"Блок может поддерживать растение-предмет\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"Если заданный блок может поддерживать заданное растение-предмет\",\n    \"operator.integrateddynamics.block.plantage\": \"Возраст растения-блока\",\n    \"operator.integrateddynamics.block.plantage.info\": \"Возраст заданного растения-блока\",\n    \"operator.integrateddynamics.block.blockbyname\": \"Блок по имени\",\n    \"operator.integrateddynamics.block.blockproperties\": \"Свойства блока\",\n    \"operator.integrateddynamics.block.blockproperties.info\": \"Получить свойства блока в виде составного тега NBT.\",\n    \"operator.integrateddynamics.block.blockfromproperties\": \"Блок из свойств\",\n    \"operator.integrateddynamics.block.blockfromproperties.info\": \"Получить заданный блок с примененными заданными свойствами.\",\n    \"operator.integrateddynamics.block.blockpossibleproperties\": \"Возможные свойства блока\",\n    \"operator.integrateddynamics.itemstack\": \"Предмет\",\n    \"operator.integrateddynamics.itemstack.basename\": \"Предмет %s\",\n    \"operator.integrateddynamics.itemstack.size\": \"Размер стека\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"Текущий размер стека\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"Максимальный размер\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"Максимальный размер стека\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"Можно сложить\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"Если стек предметов может содержать как минимум два предмета.\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"Подвержен повреждениям\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"Если предмет может получать повреждения.\",\n    \"operator.integrateddynamics.itemstack.damage\": \"Повреждение\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"Текущее повреждение предмета\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"Максимальное повреждение\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"Максимальное повреждение предмета\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"Зачарован\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"Если предмет зачарован\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"Можно зачаровать\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"Если предмет можно зачаровать\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"Стоимость ремонта\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"Стоимость ремонта предмета\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"Редкость\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"Редкость предмета\",\n    \"operator.integrateddynamics.itemstack.strength\": \"Прочность против блока\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"Прочность, которую этот предмет имеет против заданного блока\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"Может собирать\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"Если предмет может собирать заданный блок\",\n    \"operator.integrateddynamics.itemstack.block\": \"Блок\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"Блок из заданного предмета\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"Содержит жидкость\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"Если заданный предмет содержит жидкость\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"Жидкость\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"Жидкость из заданного предмета\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"Вместимость жидкости\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"Вместимость жидкости заданного предмета в мБ\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"Равенство NBT\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"Если компоненты данных заданных предметов равны\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt\": \"Равенство предметов без NBT\",\n    \"operator.integrateddynamics.itemstack.israwitemequal\": \"Равенство сырого предмета\",\n    \"operator.integrateddynamics.itemstack.mod\": \"Мод\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"Имя мода, которому принадлежит заданный предмет\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"Время горения топлива\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"Время горения топлива в тиках для заданного предмета\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"Является топливом\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"Если заданный предмет может использоваться как топливо\",\n    \"operator.integrateddynamics.itemstack.tag.info\": \"Имена тегов (строки) заданного предмета\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"Предмет имеет инвентарь\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"Если заданный предмет имеет инвентарь\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"Размер инвентаря предмета\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"Получить размер инвентаря содержимого заданного предмета\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"Инвентарь предмета\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"Получить инвентарь содержимого заданного предмета\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"Предмет с размером стека\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"Установить размер стека для заданного предмета\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"Предмет по имени\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"Счетчик предметов в списке\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"Получить общее количество точно заданного предмета в списке.\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"NBT предмета\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"Получить данные заданного предмета в виде NBT.\",\n    \"operator.integrateddynamics.itemstack.hasnbt\": \"Имеет NBT\",\n    \"operator.integrateddynamics.itemstack.hasnbt.info\": \"Если стек предметов имеет компоненты данных.\",\n    \"operator.integrateddynamics.itemstack.datakeys\": \"Ключи данных\",\n    \"operator.integrateddynamics.itemstack.datakeys.info\": \"Получить все ключи компонентов данных стека предметов.\",\n    \"operator.integrateddynamics.itemstack.datavalue\": \"Значение данных\",\n    \"operator.integrateddynamics.itemstack.datavalue.info\": \"Получить значение заданного ключа компонента данных.\",\n    \"operator.integrateddynamics.itemstack.withdata\": \"С данными\",\n    \"operator.integrateddynamics.itemstack.withdata.info\": \"Добавить заданный ключ и значение компонента данных к заданному стеку предметов.\",\n    \"operator.integrateddynamics.entity\": \"Сущность\",\n    \"operator.integrateddynamics.entity.basename\": \"Сущность %s\",\n    \"operator.integrateddynamics.entity.ismob\": \"Это моб\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"Если сущность является мобом\",\n    \"operator.integrateddynamics.entity.isanimal\": \"Это животное\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"Если сущность является животным\",\n    \"operator.integrateddynamics.entity.isitem\": \"Это предмет\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"Если сущность является предметом\",\n    \"operator.integrateddynamics.entity.isplayer\": \"Это игрок\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"Если сущность является игроком\",\n    \"operator.integrateddynamics.entity.isminecart\": \"Это вагонетка\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"Если сущность является вагонеткой\",\n    \"operator.integrateddynamics.entity.item\": \"Предмет\",\n    \"operator.integrateddynamics.entity.item.info\": \"Предмет из сущности-предмета\",\n    \"operator.integrateddynamics.entity.health\": \"Здоровье\",\n    \"operator.integrateddynamics.entity.health.info\": \"Здоровье сущности\",\n    \"operator.integrateddynamics.entity.width\": \"Ширина\",\n    \"operator.integrateddynamics.entity.width.info\": \"Ширина сущности\",\n    \"operator.integrateddynamics.entity.height\": \"Высота\",\n    \"operator.integrateddynamics.entity.height.info\": \"Высота сущности\",\n    \"operator.integrateddynamics.entity.isburning\": \"Горит\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"Если сущность горит\",\n    \"operator.integrateddynamics.entity.iswet\": \"Мокрая\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"Если сущность мокрая\",\n    \"operator.integrateddynamics.entity.iscrouching\": \"Приседает\",\n    \"operator.integrateddynamics.entity.iscrouching.info\": \"Если сущность приседает\",\n    \"operator.integrateddynamics.entity.iseating\": \"Ест\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"Если сущность ест\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"Инвентарь брони\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"Список предметов, которые сущность носит в качестве брони.\",\n    \"operator.integrateddynamics.entity.inventory\": \"Инвентарь игрока\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"Список предметов, которые игрок носит с собой.\",\n    \"operator.integrateddynamics.entity.mod\": \"Мод\",\n    \"operator.integrateddynamics.entity.mod.info\": \"Имя мода, которому принадлежит данная сущность\",\n    \"operator.integrateddynamics.entity.targetblock\": \"Целевой блок\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"Блок, на который в данный момент смотрит сущность.\",\n    \"operator.integrateddynamics.entity.targetentity\": \"Целевая сущность\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"Сущность, на которую в данный момент смотрит данная сущность.\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"Открыт интерфейс\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"Если у данного игрока открыт внешний графический интерфейс.\",\n    \"operator.integrateddynamics.entity.helditem\": \"Предмет в основной руке\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"Предмет, который данная сущность в данный момент держит в основной руке.\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"Предмет в побочной руке\",\n    \"operator.integrateddynamics.entity.mounted\": \"Сидящие сущности\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"Сущности, которые в данный момент находятся на данной сущности.\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"Содержимое рамки предметов\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"Содержимое данной рамки предметов.\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"Вращение рамки предметов\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"Вращение данной рамки предметов.\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"Звук боли сущности\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"Звук боли данной сущности.\",\n    \"operator.integrateddynamics.entity.deathsound\": \"Звук смерти сущности\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"Звук смерти данной сущности.\",\n    \"operator.integrateddynamics.entity.age\": \"Возраст сущности\",\n    \"operator.integrateddynamics.entity.age.info\": \"Возраст данной сущности.\",\n    \"operator.integrateddynamics.entity.ischild\": \"Сущность является ребенком\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"Если данная сущность является ребенком.\",\n    \"operator.integrateddynamics.entity.canbreed\": \"Сущность может размножаться\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"Если данная сущность готова к размножению.\",\n    \"operator.integrateddynamics.entity.isinlove\": \"Сущность влюблена\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"Если данная сущность влюблена и готова к размножению.\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"Сущность может размножаться с помощью\",\n    \"operator.integrateddynamics.entity.isshearable\": \"Сущность поддается стрижке\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"Если данную сущность можно стричь.\",\n    \"operator.integrateddynamics.entity.nbt\": \"NBT сущности\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"Получить данную сущность в виде NBT.\",\n    \"operator.integrateddynamics.entity.entitytype\": \"Тип сущности\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"Имя типа сущности.\",\n    \"operator.integrateddynamics.entity.entityitems\": \"Предметы сущности\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"Предметы, содержащиеся в этой сущности.\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"Жидкости сущности\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"Жидкости, содержащиеся в этой сущности.\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"Накопленная энергия сущности\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"Количество энергии, накопленной в этой сущности.\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"Вместимость энергии сущности\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"Вместимость энергии этой сущности.\",\n    \"operator.integrateddynamics.entity.entityitemtooltip\": \"Подсказка сущности\",\n    \"operator.integrateddynamics.entity.entityitemtooltip.info\": \"В контексте данной сущности игрока получить подсказку для указанного предмета в виде списка строк.\",\n    \"operator.integrateddynamics.itemstack.tooltip\": \"Подсказка\",\n    \"operator.integrateddynamics.itemstack.tooltip.info\": \"Получить подсказку для данного предмета в виде списка строк.\",\n    \"operator.integrateddynamics.fluidstack\": \"Жидкость\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"Жидкость %s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"Количество\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"Количество в мБ\",\n    \"operator.integrateddynamics.fluidstack.block\": \"Блок\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"Блок жидкости\",\n    \"operator.integrateddynamics.fluidstack.light_level\": \"Уровень света\",\n    \"operator.integrateddynamics.fluidstack.light_level.info\": \"Уровень света, излучаемый жидкостью\",\n    \"operator.integrateddynamics.fluidstack.density\": \"Плотность\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"Плотность жидкости\",\n    \"operator.integrateddynamics.fluidstack.temperature\": \"Температура\",\n    \"operator.integrateddynamics.fluidstack.temperature.info\": \"Температура жидкости\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"Вязкость\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"Вязкость жидкости\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air\": \"Легче воздуха\",\n    \"operator.integrateddynamics.fluidstack.lighter_than_air.info\": \"Если жидкость легче воздуха\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"Редкость\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"Редкость жидкости\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize\": \"Звук испарения жидкости\",\n    \"operator.integrateddynamics.fluidstack.sound_fluid_vaporize.info\": \"Звук испарения для данной жидкости\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty\": \"Звук опустошения ведра\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_empty.info\": \"Звук опустошения ведра для данной жидкости\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill\": \"Звук заполнения ведра\",\n    \"operator.integrateddynamics.fluidstack.sound_bucket_fill.info\": \"Звук заполнения ведра для данной жидкости\",\n    \"operator.integrateddynamics.fluidstack.bucket\": \"Ведро\",\n    \"operator.integrateddynamics.fluidstack.bucket.info\": \"Заполненное ведро для данной жидкости\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal\": \"Сырые жидкости равны\",\n    \"operator.integrateddynamics.fluidstack.mod\": \"Мод\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"Имя мода, которому принадлежит данная жидкость\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"NBT жидкости\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"Получить данные данной жидкости в виде NBT.\",\n    \"operator.integrateddynamics.fluidstack.with_amount\": \"Жидкость с количеством\",\n    \"operator.integrateddynamics.fluidstack.with_amount.info\": \"Скопировать данную жидкость с указанным количеством\",\n    \"operator.integrateddynamics.fluidstack.datakeys\": \"Ключи данных\",\n    \"operator.integrateddynamics.fluidstack.datakeys.info\": \"Получить все ключи компонентов данных стека жидкости.\",\n    \"operator.integrateddynamics.fluidstack.datavalue\": \"Значение данных\",\n    \"operator.integrateddynamics.fluidstack.datavalue.info\": \"Получить значение данного ключа компонента данных.\",\n    \"operator.integrateddynamics.fluidstack.withdata\": \"С данными\",\n    \"operator.integrateddynamics.fluidstack.withdata.info\": \"Добавить указанный ключ и значение компонента данных к данному стеку жидкости.\",\n    \"operator.integrateddynamics.nbt.compound_size\": \"Размер составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_size.info\": \"Количество записей внутри заданного составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_keys\": \"Ключи составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_keys.info\": \"Список ключей внутри заданного составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_haskey\": \"Составной тег NBT содержит ключ\",\n    \"operator.integrateddynamics.nbt.compound_haskey.info\": \"Если заданный составной тег NBT содержит указанный ключ\",\n    \"operator.integrateddynamics.nbt.compound_type\": \"Тип записи составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_type.info\": \"Тип значения в заданном составном теге NBT, соответствующий указанному ключу\",\n    \"operator.integrateddynamics.nbt.compound_value_tag\": \"Значение составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_value_tag.info\": \"Значение любого типа в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean\": \"Значение составного тега NBT (логическое)\",\n    \"operator.integrateddynamics.nbt.compound_value_boolean.info\": \"Логическое значение в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_integer\": \"Значение составного тега NBT (целое число)\",\n    \"operator.integrateddynamics.nbt.compound_value_integer.info\": \"Целочисленное значение в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_long\": \"Значение составного тега NBT (длинное целое)\",\n    \"operator.integrateddynamics.nbt.compound_value_long.info\": \"Длинное целочисленное значение в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_double\": \"Значение составного тега NBT (число с плавающей точкой)\",\n    \"operator.integrateddynamics.nbt.compound_value_double.info\": \"Числовое значение с плавающей точкой в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_string\": \"Значение составного тега NBT (строка)\",\n    \"operator.integrateddynamics.nbt.compound_value_string.info\": \"Строковое значение в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_compound\": \"Значение составного тега NBT (составной тег)\",\n    \"operator.integrateddynamics.nbt.compound_value_compound.info\": \"Составное значение в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag\": \"Значение составного тега NBT (список NBT)\",\n    \"operator.integrateddynamics.nbt.compound_value_list_tag.info\": \"Список NBT в заданном составном теге NBT с указанным ключом\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte\": \"Значение составного тега NBT (массив байтов)\",\n    \"operator.integrateddynamics.nbt.compound_value_list_byte.info\": \"Массив байтов в заданном составном теге NBT с указанным ключом как список целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int\": \"Значение составного тега NBT (массив целых чисел)\",\n    \"operator.integrateddynamics.nbt.compound_value_list_int.info\": \"Массив целых чисел в заданном составном теге NBT с указанным ключом как список целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long\": \"Значение составного тега NBT (массив длинных целых)\",\n    \"operator.integrateddynamics.nbt.compound_value_list_long.info\": \"Массив длинных целых чисел в заданном составном теге NBT с указанным ключом как список длинных целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_without\": \"Составной тег NBT без ключа\",\n    \"operator.integrateddynamics.nbt.compound_without.info\": \"Получить копию заданного составного тега NBT без указанного ключа\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean\": \"Составной тег NBT с логическим значением\",\n    \"operator.integrateddynamics.nbt.compound_with_boolean.info\": \"Получить копию заданного составного тега NBT с добавленной логической записью\",\n    \"operator.integrateddynamics.nbt.compound_with_short\": \"Составной тег NBT с коротким целым\",\n    \"operator.integrateddynamics.nbt.compound_with_short.info\": \"Получить копию заданного составного тега NBT с добавленным коротким целым числом\",\n    \"operator.integrateddynamics.nbt.compound_with_integer\": \"Составной тег NBT с целым числом\",\n    \"operator.integrateddynamics.nbt.compound_with_integer.info\": \"Получить копию заданного составного тега NBT с добавленной целочисленной записью\",\n    \"operator.integrateddynamics.nbt.compound_with_long\": \"Составной тег NBT с длинным целым\",\n    \"operator.integrateddynamics.nbt.compound_with_long.info\": \"Получить копию заданного составного тега NBT с добавленной записью длинного целого числа\",\n    \"operator.integrateddynamics.nbt.compound_with_double\": \"Составной тег NBT с числом с плавающей точкой двойной точности\",\n    \"operator.integrateddynamics.nbt.compound_with_double.info\": \"Получить копию заданного составного тега NBT с добавленной записью числа с плавающей точкой двойной точности\",\n    \"operator.integrateddynamics.nbt.compound_with_float\": \"Составной тег NBT с числом с плавающей точкой одинарной точности\",\n    \"operator.integrateddynamics.nbt.compound_with_float.info\": \"Получить копию заданного составного тега NBT с добавленной записью числа с плавающей точкой одинарной точности\",\n    \"operator.integrateddynamics.nbt.compound_with_string\": \"Составной тег NBT со строкой\",\n    \"operator.integrateddynamics.nbt.compound_with_string.info\": \"Получить копию заданного составного тега NBT с добавленной строковой записью\",\n    \"operator.integrateddynamics.nbt.compound_with_tag\": \"Составной тег NBT с тегом NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_tag.info\": \"Получить копию заданного составного тега NBT с добавленной записью тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag\": \"Составной тег NBT со списком NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_list_tag.info\": \"Получить копию заданного составного тега NBT с добавленной записью списка NBT\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte\": \"Составной тег NBT со списком байтов\",\n    \"operator.integrateddynamics.nbt.compound_with_list_byte.info\": \"Получить копию заданного составного тега NBT с добавленной записью массива байтов как списка целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int\": \"Составной тег NBT со списком целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_with_list_int.info\": \"Получить копию заданного составного тега NBT с добавленной записью массива целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long\": \"Составной тег NBT со списком длинных целых\",\n    \"operator.integrateddynamics.nbt.compound_with_list_long.info\": \"Получить копию заданного составного тега NBT с добавленной записью массива длинных целых чисел\",\n    \"operator.integrateddynamics.nbt.compound_subset\": \"Подмножество составного тега NBT\",\n    \"operator.integrateddynamics.nbt.compound_subset.info\": \"Если первый составной тег NBT является подмножеством или равен второму составному тегу NBT. Это будет рекурсивно проверять вложенные теги.\",\n    \"operator.integrateddynamics.nbt.compound_union\": \"Объединение составных тегов NBT\",\n    \"operator.integrateddynamics.nbt.compound_intersection\": \"Пересечение составных тегов NBT\",\n    \"operator.integrateddynamics.nbt.compound_minus\": \"Разность составных тегов NBT\",\n    \"operator.integrateddynamics.nbt.as_boolean\": \"Тег NBT Byte как логическое значение\",\n    \"operator.integrateddynamics.nbt.as_boolean.info\": \"Получить логическое значение заданного тега NBT Byte\",\n    \"operator.integrateddynamics.nbt.as_byte\": \"Тег NBT Byte как целое число\",\n    \"operator.integrateddynamics.nbt.as_byte.info\": \"Получить целочисленное значение заданного тега NBT Byte\",\n    \"operator.integrateddynamics.nbt.as_short\": \"Тег NBT Short как целое число\",\n    \"operator.integrateddynamics.nbt.as_short.info\": \"Получить целочисленное значение заданного тега NBT Short\",\n    \"operator.integrateddynamics.nbt.as_int\": \"Тег NBT Integer как целое число\",\n    \"operator.integrateddynamics.nbt.as_int.info\": \"Получить целочисленное значение заданного тега NBT Integer\",\n    \"operator.integrateddynamics.nbt.as_long\": \"Тег NBT Long как длинное целое\",\n    \"operator.integrateddynamics.nbt.as_long.info\": \"Получить значение длинного целого числа заданного тега NBT Long\",\n    \"operator.integrateddynamics.nbt.as_double\": \"Тег NBT Double как число с плавающей точкой\",\n    \"operator.integrateddynamics.nbt.as_double.info\": \"Получить значение числа с плавающей точкой заданного тега NBT Double\",\n    \"operator.integrateddynamics.nbt.as_float\": \"Тег NBT Float как число с плавающей точкой\",\n    \"operator.integrateddynamics.nbt.as_float.info\": \"Получить значение числа с плавающей точкой заданного тега NBT Float\",\n    \"operator.integrateddynamics.nbt.as_string\": \"Тег NBT String как строка\",\n    \"operator.integrateddynamics.nbt.as_string.info\": \"Получить строковое значение заданного тега NBT String\",\n    \"operator.integrateddynamics.nbt.as_tag_list\": \"Тег NBT List как список NBT\",\n    \"operator.integrateddynamics.nbt.as_tag_list.info\": \"Получить значение списка NBT заданного тега NBT List\",\n    \"operator.integrateddynamics.nbt.as_byte_list\": \"Тег NBT Byte Array как список байтов\",\n    \"operator.integrateddynamics.nbt.as_byte_list.info\": \"Получить значение списка байтов заданного тега NBT Byte Array\",\n    \"operator.integrateddynamics.nbt.as_int_list\": \"Тег NBT Integer Array как список целых чисел\",\n    \"operator.integrateddynamics.nbt.as_int_list.info\": \"Получить значение списка целых чисел заданного тега NBT Integer Array\",\n    \"operator.integrateddynamics.nbt.as_long_list\": \"Тег NBT Long Array как список длинных целых\",\n    \"operator.integrateddynamics.nbt.as_long_list.info\": \"Получить значение списка длинных целых чисел заданного тега NBT Long Array\",\n    \"operator.integrateddynamics.nbt.from_boolean\": \"Тег NBT Byte из логического значения\",\n    \"operator.integrateddynamics.nbt.from_boolean.info\": \"Создать тег NBT Byte из заданного логического значения\",\n    \"operator.integrateddynamics.nbt.from_short\": \"Тег NBT Short из целого числа\",\n    \"operator.integrateddynamics.nbt.from_short.info\": \"Создать тег NBT Short из заданного целочисленного значения\",\n    \"operator.integrateddynamics.nbt.from_byte\": \"Тег NBT Byte из целого числа\",\n    \"operator.integrateddynamics.nbt.from_byte.info\": \"Создать тег NBT Byte из заданного целочисленного значения\",\n    \"operator.integrateddynamics.nbt.from_int\": \"Тег NBT Integer из целого числа\",\n    \"operator.integrateddynamics.nbt.from_int.info\": \"Создать тег NBT Integer из заданного целочисленного значения\",\n    \"operator.integrateddynamics.nbt.from_long\": \"Тег NBT Long из длинного целого\",\n    \"operator.integrateddynamics.nbt.from_long.info\": \"Создать тег NBT Long из заданного значения длинного целого числа\",\n    \"operator.integrateddynamics.nbt.from_double\": \"Тег NBT Double из числа с плавающей точкой\",\n    \"operator.integrateddynamics.nbt.from_double.info\": \"Создать тег NBT Double из заданного значения числа с плавающей точкой\",\n    \"operator.integrateddynamics.nbt.from_float\": \"Тег NBT Float из числа с плавающей точкой\",\n    \"operator.integrateddynamics.nbt.from_string\": \"Тег NBT String из строки\",\n    \"operator.integrateddynamics.nbt.from_string.info\": \"Создать тег NBT String из заданного строкового значения\",\n    \"operator.integrateddynamics.nbt.from_tag_list\": \"Тег NBT List из списка NBT\",\n    \"operator.integrateddynamics.nbt.from_tag_list.info\": \"Создать тег NBT List из заданного значения списка NBT\",\n    \"operator.integrateddynamics.nbt.from_byte_list\": \"Тег NBT Byte Array из списка байтов\",\n    \"operator.integrateddynamics.nbt.from_byte_list.info\": \"Создать тег NBT Byte Array из заданного значения списка байтов\",\n    \"operator.integrateddynamics.nbt.from_int_list\": \"Тег NBT Integer Array из списка целых чисел\",\n    \"operator.integrateddynamics.nbt.from_int_list.info\": \"Создать тег NBT Integer Array из заданного значения списка целых чисел\",\n    \"operator.integrateddynamics.nbt.from_long_list\": \"Тег NBT Long Array из списка длинных целых\",\n    \"operator.integrateddynamics.nbt.from_long_list.info\": \"Создать тег NBT Long Array из заданного значения списка длинных целых чисел\",\n    \"operator.integrateddynamics.nbt.path_match_first\": \"Первое совпадение по пути NBT\",\n    \"operator.integrateddynamics.nbt.path_match_first.info\": \"Применить заданное выражение пути NBT к заданному значению NBT и вернуть первое совпадение\",\n    \"operator.integrateddynamics.nbt.path_match_all\": \"Все совпадения по пути NBT\",\n    \"operator.integrateddynamics.nbt.path_match_all.info\": \"Применить заданное выражение пути NBT к заданному значению NBT и вернуть все совпадения в виде списка\",\n    \"operator.integrateddynamics.nbt.path_test\": \"Тест пути NBT\",\n    \"operator.integrateddynamics.nbt.path_test.info\": \"Проверить, соответствует ли заданное выражение пути NBT заданному значению NBT\",\n    \"operator.integrateddynamics.operator\": \"Оператор\",\n    \"operator.integrateddynamics.operator.basename\": \"Оператор %s\",\n    \"operator.integrateddynamics.operator.apply\": \"Применить\",\n    \"operator.integrateddynamics.operator.apply.info\": \"Применить заданный оператор к заданному значению.\",\n    \"operator.integrateddynamics.operator.apply2\": \"Применить 2\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"Применить заданный оператор к двум заданным значениям.\",\n    \"operator.integrateddynamics.operator.apply3\": \"Применить 3\",\n    \"operator.integrateddynamics.operator.apply_n\": \"Применить N\",\n    \"operator.integrateddynamics.operator.apply_n.info\": \"Применить заданный оператор к списку заданных значений.\",\n    \"operator.integrateddynamics.operator.apply0\": \"Применить 0\",\n    \"operator.integrateddynamics.operator.apply0.info\": \"Применить заданный оператор без аргументов.\",\n    \"operator.integrateddynamics.operator.map\": \"Отобразить\",\n    \"operator.integrateddynamics.operator.map.info\": \"Применить заданный оператор ко всем элементам списка, создав новый список отображенных значений.\",\n    \"operator.integrateddynamics.operator.filter\": \"Фильтровать\",\n    \"operator.integrateddynamics.operator.filter.info\": \"Фильтровать список элементов, сопоставляя их с заданным предикатом.\",\n    \"operator.integrateddynamics.operator.conjunction\": \"Конъюнкция\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"Выполняет конъюнкцию двух предикатов.\",\n    \"operator.integrateddynamics.operator.disjunction\": \"Дизъюнкция\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"Выполняет дизъюнкцию двух предикатов.\",\n    \"operator.integrateddynamics.operator.negation\": \"Отрицание\",\n    \"operator.integrateddynamics.operator.negation.info\": \"Выполняет отрицание предиката.\",\n    \"operator.integrateddynamics.operator.pipe\": \"Композиция\",\n    \"operator.integrateddynamics.operator.pipe.info\": \"Создать новый оператор, который передает выходные данные первого оператора во второй оператор.\",\n    \"operator.integrateddynamics.operator.pipe2\": \"Композиция 2\",\n    \"operator.integrateddynamics.operator.pipe2.info\": \"Создать новый оператор, который передает входные данные в первый и второй операторы, а затем объединяет их выходные данные в третий оператор.\",\n    \"operator.integrateddynamics.operator.flip\": \"Перестановка\",\n    \"operator.integrateddynamics.operator.flip.info\": \"Меняет местами первые два входных параметра оператора.\",\n    \"operator.integrateddynamics.virtual.flipped\": \"Переставленный\",\n    \"operator.integrateddynamics.operator.reduce\": \"Свертка\",\n    \"operator.integrateddynamics.operator.reduce.info\": \"Применить заданный оператор ко всем элементам списка для сведения списка к одному значению.\",\n    \"operator.integrateddynamics.operator.reduce1\": \"Свертка 1\",\n    \"operator.integrateddynamics.operator.reduce1.info\": \"Применить заданный оператор ко всем элементам списка для сведения списка к одному значению. reduce1(op, list) эквивалентно reduce(op, tail(list), head(list)).\",\n    \"operator.integrateddynamics.operator.by_name\": \"Оператор по имени\",\n    \"operator.integrateddynamics.operator.by_name.info\": \"Получить оператор с заданным уникальным именем.\",\n    \"operator.integrateddynamics.virtual.recipeoutputbyinput\": \"Выход рецепта по входу\",\n    \"operator.integrateddynamics.virtual.recipeinputsbyoutput\": \"Входы рецепта по выходу\",\n    \"operator.integrateddynamics.virtual.recipesbyinput\": \"Рецепты по входу\",\n    \"operator.integrateddynamics.virtual.recipesbyoutput\": \"Рецепты по выходу\",\n    \"operator.integrateddynamics.ingredients\": \"Ингредиенты\",\n    \"operator.integrateddynamics.ingredients.basename\": \"Ингредиенты %s\",\n    \"operator.integrateddynamics.ingredients.items\": \"Предметы ингредиентов\",\n    \"operator.integrateddynamics.ingredients.items.info\": \"Список предметов\",\n    \"operator.integrateddynamics.ingredients.fluids\": \"Жидкости ингредиентов\",\n    \"operator.integrateddynamics.ingredients.fluids.info\": \"Список жидкостей\",\n    \"operator.integrateddynamics.ingredients.energies\": \"Энергетические элементы ингредиентов\",\n    \"operator.integrateddynamics.ingredients.energies.info\": \"Список энергетических элементов\",\n    \"operator.integrateddynamics.ingredients.with_item\": \"Ингредиенты с предметом\",\n    \"operator.integrateddynamics.ingredients.with_item.info\": \"Получить копию заданных ингредиентов с указанным предметом в заданной позиции ингредиента\",\n    \"operator.integrateddynamics.ingredients.with_fluid\": \"Ингредиенты с жидкостью\",\n    \"operator.integrateddynamics.ingredients.with_fluid.info\": \"Получить копию заданных ингредиентов с указанной жидкостью в заданной позиции ингредиента\",\n    \"operator.integrateddynamics.ingredients.with_energy\": \"Ингредиенты с энергией\",\n    \"operator.integrateddynamics.ingredients.with_energy.info\": \"Получить копию заданных ингредиентов с указанной энергией в заданной позиции ингредиента\",\n    \"operator.integrateddynamics.ingredients.with_items\": \"Ингредиенты с предметами\",\n    \"operator.integrateddynamics.ingredients.with_items.info\": \"Получить копию заданных ингредиентов с указанным списком предметов в заданной позиции ингредиента\",\n    \"operator.integrateddynamics.ingredients.with_fluids\": \"Ингредиенты с жидкостями\",\n    \"operator.integrateddynamics.ingredients.with_fluids.info\": \"Получить копию заданных ингредиентов с указанным списком жидкостей в заданной позиции ингредиента\",\n    \"operator.integrateddynamics.ingredients.with_energies\": \"Ингредиенты с энергетическими элементами\",\n    \"operator.integrateddynamics.ingredients.with_energies.info\": \"Получить копию заданных ингредиентов с указанным списком энергетических элементов в заданной позиции ингредиента\",\n    \"operator.integrateddynamics.recipe\": \"Рецепт\",\n    \"operator.integrateddynamics.recipe.basename\": \"Рецепт %s\",\n    \"operator.integrateddynamics.recipe.input\": \"Входные ингредиенты рецепта\",\n    \"operator.integrateddynamics.recipe.input.info\": \"Входные ингредиенты заданного рецепта\",\n    \"operator.integrateddynamics.recipe.output\": \"Выходные ингредиенты рецепта\",\n    \"operator.integrateddynamics.recipe.output.info\": \"Выходные ингредиенты заданного рецепта\",\n    \"operator.integrateddynamics.recipe.with_input\": \"Рецепт с входными ингредиентами\",\n    \"operator.integrateddynamics.recipe.with_input.info\": \"Получить копию заданного рецепта с указанными ингредиентами в качестве входных\",\n    \"operator.integrateddynamics.recipe.with_output\": \"Рецепт с выходными ингредиентами\",\n    \"operator.integrateddynamics.recipe.with_input_output\": \"Рецепт с входными и выходными ингредиентами\",\n    \"operator.integrateddynamics.recipe.with_input_output.info\": \"Создать рецепт с первыми ингредиентами в качестве входных и вторыми ингредиентами в качестве выходных\",\n    \"operator.integrateddynamics.general\": \"Общее\",\n    \"operator.integrateddynamics.general.basename\": \"Общее %s\",\n    \"operator.integrateddynamics.general.choice\": \"Выбор\",\n    \"operator.integrateddynamics.general.choice.info\": \"Если первое значение истинно, выбирается второе значение, иначе третье.\\\\nТипы второго и третьего значения должны совпадать.\",\n    \"operator.integrateddynamics.general.identity\": \"Идентичность\",\n    \"operator.integrateddynamics.general.identity.info\": \"Копия входного значения.\",\n    \"operator.integrateddynamics.general.constant\": \"Константа\",\n    \"operator.integrateddynamics.general.constant.info\": \"Копия первого входного значения.\",\n    \"operator.integrateddynamics.general.isnull\": \"Является Null\",\n    \"operator.integrateddynamics.general.isnull.info\": \"Если заданное значение является null\",\n    \"operator.integrateddynamics.general.isnotnull\": \"Не является Null\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"Если заданное значение не является null\",\n    \"operator.integrateddynamics.virtual\": \"Виртуальный оператор\",\n    \"operator.integrateddynamics.virtual.basename\": \"Виртуальный %s\",\n    \"operator.integrateddynamics.virtual.p_conjunction\": \"Конъюнкция предикатов\",\n    \"operator.integrateddynamics.virtual.p_conjunction.info\": \"Конъюнкция двух предикатов\",\n    \"operator.integrateddynamics.virtual.p_disjunction\": \"Дизъюнкция предикатов\",\n    \"operator.integrateddynamics.virtual.p_disjunction.info\": \"Дизъюнкция двух предикатов\",\n    \"operator.integrateddynamics.virtual.p_negation\": \"Отрицание предиката\",\n    \"operator.integrateddynamics.virtual.p_negation.info\": \"Отрицание предиката\",\n    \"operator.integrateddynamics.virtual.piped\": \"Цепочка операторов\",\n    \"operator.integrateddynamics.virtual.piped.info\": \"Цепочка операторов\",\n    \"operator.integrateddynamics.virtual.piped2\": \"Цепочка из двух операторов\",\n    \"operator.integrateddynamics.virtual.piped2.info\": \"Цепочка из двух связанных операторов\",\n    \"operator.integrateddynamics.cast\": \"Приведение типов\",\n    \"operator.integrateddynamics.cast.basename\": \"Приведение\",\n    \"operator.integrateddynamics.cast.tooltip\": \"Приведение %s к %s\",\n    \"operator.integrateddynamics.parse\": \"Анализ\",\n    \"operator.integrateddynamics.parse.basename\": \"Анализ\",\n    \"operator.integrateddynamics.parse.tooltip\": \"Анализ %s\",\n    \"info_book.integrateddynamics.section.main\": \"О динамике интеграции\",\n    \"info_book.integrateddynamics.tag_index\": \"&oОглавление&r\",\n    \"info_book.integrateddynamics.introduction\": \"Введение\",\n    \"info_book.integrateddynamics.introduction.text1\": \"Слишком долго возможности автоматизации были слишком ограничены. &1Integrated Dynamics&0 стремится стать основным модом для автоматизации ваших логических систем. Он предлагает выразительный и погружной опыт программирования логики с поддержкой большинства элементов &lMinecraft&r и его модификаций.\",\n    \"info_book.integrateddynamics.introduction.text2\": \"Красные цепи всегда были очень простым, но работоспособным и легко понятным методом автоматизации. Однако простота красного камня также является неудобством. Для сетей автоматизации с высокой сложностью красные цепи могут стать очень громоздкими и, следовательно, трудными для управления. Кроме того, одна маленькая утечка воды может вызвать серьезные проблемы у каждого инженера по красному камню.\",\n    \"info_book.integrateddynamics.introduction.text4\": \"Вы можете перемещаться по этой книге, нажимая на стрелки внизу; при удерживании Shift вы будете перелистывать целые разделы вместо отдельных страниц. В начале больших разделов вы увидите оглавление с гиперссылками на подразделы для быстрого просмотра. Когда вы встретите рецепты, предметы, описанные в этой книге, будут кликабельны как быстрая ссылка на раздел, где они объяснены. Вы также заметите кнопку в верхнем левом углу этой книги, которая приведет вас к разделу выше текущего. Кнопка в правом верхнем углу будет возвращать вас к предыдущей посещенной странице при каждом нажатии.\",\n    \"info_book.integrateddynamics.tutorials\": \"Учебники\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"Введение\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"Эта часть книги содержит несколько учебников, которые помогут вам понять основы мода. Кроме того, вы сможете получить некоторые &lнаграды&r, выполняя достижения каждого учебника.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"Основы Менрила\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"Чтобы начать работу с этим модом, вам нужно найти несколько &lДеревьев Менрила&r, которые чаще всего встречаются в &lБиомах Менеглина&r.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"Если вы хотите повторить процесс, вы можете сбросить &lПресс&r, подав на него импульс красного камня.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"В будущем вы также сможете создавать улучшенные версии этих машин, которые легче автоматизировать, но они более дорогие: &lМеханический Пресс&r и &lМеханический Сушильный Бассейн&r.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"Основы Сетей\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"Главная цель этого мода — создание управляющих сетей для автоматизации. Это делается с помощью &lЛогических Кабелей&r. Попробуйте создать сеть из десяти таких кабелей.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"Следующим шагом будет создание нескольких &lКарт Переменных&r и &lТрансформаторов Переменных&r. &lКарты Переменных&r используются для хранения ссылок на значения в сети. &lТрансформаторы Переменных&r — это элементы для создания компонентов, которые можно разместить на &lЛогических Кабелях&r для чтения и записи этих значений.\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"Основные Компоненты Сети\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"Один из способов связывания &lКарт Переменных&r с определенными значениями — это считывание значений с помощью &lСчитывателей&r. Давайте начнем с создания некоторых базовых считывателей. После этого создайте &lДисплей&r, который полезен для отображения значений &lКарт Переменных&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"Аспекты\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"В качестве простого примера, попробуем считать значение красного камня и отобразить его.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"После этого щелкните правой кнопкой мыши по &lСчитывателю Красного Камня&r, чтобы открыть графический интерфейс, затем вставьте &lКарту Переменной&r в аспект &6Целочисленное&0 &lЗначение Красного Камня&r, чтобы связать значение красного камня цели с этой &lКартой Переменной&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"В качестве следующего шага создайте &lЗаписывающее Устройство Красного Камня&r и заставьте его записывать максимальное целочисленное значение красного камня, считанное &lСчитывателем Красного Камня&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"Логические Операции\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"Хотя отображение значений, полученных от считывателей, может быть полезным, всё становится действительно интересным, когда вы начинаете комбинировать определённые значения с другими значениями с помощью &2Операторов&0. Все &2Операторы&0 можно найти в &lПрограмматоре Логики&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"Начните с создания &lПрограмматора Логики&r, который существует как в виде предмета, так и в виде блока. Откройте его графический интерфейс, чтобы увидеть список всех &lоператоров&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"Далее найдите оператор &6Целочисленный&0 и нажмите на него. Это откроет окно, в котором вам нужно ввести число. После того, как вы это сделаете, вставьте пустую &lКарту Переменной&r, чтобы связать это &6Целое Число&0 с &lКартой Переменной&r. Когда закончите, повторите это ещё раз для другого &6Целого Числа&0.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"Наконец, найдите оператор &lСложение&r и откройте его. Это позволит вам сложить два &eЧисла&0. Вставьте свои две &6Целочисленные&0 &lКарты Переменных&r в эти слоты и свяжите их с новой &lКартой Переменной&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"Обработка рецептов\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text1\": \"&oРецепты&r являются важной частью &lMinecraft&r. Именно поэтому этот мод предлагает поддержку первого уровня для них через значения &8Рецептов&0 и значения &lИнгредиентов&r, чтобы представлять их входные и выходные наборы. В этом уроке мы рассмотрим, как можно получить и использовать рецепты.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text2\": \"Проще всего получить &8Рецепты&0 — просто считать их с машины, которая может обрабатывать рецепты. Итак, начнем с чтения всех крафтовых рецептов, разместив &lСчитыватель Машин&r рядом с &lВерстаком&r и считав список рецептов.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text3\": \"Если вы хотите получить более детальный контроль над рецептами или работаете с машиной, которая не предоставляет свои рецепты через &lСчитыватель Машин&r, вы можете визуально создать &8Рецепт&r самостоятельно в &lПрограмматоре Логики&r. Давайте попробуем создать простой рецепт, например, для создания &lСундука&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text4\": \"В этой последней части урока мы пойдем дальше в чтении рецептов. Используя аспект &lРецепт по Выходу&r, мы можем получить оператор, который, будучи примененным к экземпляру &8Ингредиентов&0, вернет первый &8Рецепт&0, который возвращает заданные &8Ингредиенты&0 в качестве выхода. Мы будем использовать это, чтобы найти рецепт &lСундука&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text5\": \"Давайте начнем с создания экземпляра &8Ингредиентов&0 с одним &8Сундуком&0 в вашем &lПрограмматоре Логики&r. Затем прочитайте аспект &lРецепт по Выходу&r из &lСчитывателя Машин&r, нацеленного на &lВерстак&r. После этого используйте оператор &oПрименить&r, чтобы применить ваш &lСундук&r &8Ингредиент&0 к оператору, полученному из &lСчитывателя Машин&r. Наконец, вставьте полученную переменную в &lДисплей&r, чтобы увидеть рецепт &lСундука&r. (Не забудьте вставить все использованные переменные в &lХранилище Переменных&r).\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text6\": \"Очевидно, что с &8Рецептами&0 и &8Ингредиентами&0 можно делать гораздо больше. Загляните в &lСчитыватель Машин&r и &lПрограмматор Логики&r, чтобы узнать о других расширенных возможностях.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text1\": \"&4NBT&0 теги используются для представления сложных структур данных. Обычно они используются внутри Minecraft для хранения информации о машинах и сущностях. В этом уроке мы рассмотрим простое чтение и манипулирование NBT-тегами.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text2\": \"В качестве первого шага используйте &lСчитыватель Блоков&r на кафедре, прочитайте её &4NBT&0 данные и визуализируйте их в &lДисплее&r.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text3\": \"Далее, из считанного &lКафедра&r &4NBT&0 тега, прочитайте подтег &3Книга&0 как составной тег и визуализируйте его.\",\n    \"info_book.integrateddynamics.tutorials.nbt.text5\": \"Чтобы выполнить финальное достижение, создайте выражение &lПуть NBT&r, которое выбирает поле &3Книга&0/&3id&0, примените его к нашему тегу &lКафедра&r и визуализируйте его в &lДисплее&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"Продвинутые Компоненты Сети\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"Теперь, когда вы знакомы с базовой логикой, мы можем взглянуть на более продвинутые компоненты сети: &lМатериализатор&r и &lПрокси&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"Как вы можете помнить, &lПрограмматор Логики&r позволяет вам создавать переменные со &остатическими&r значениями, или вы можете создавать более сложные переменные на основе &одинамической&r оценки значений.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"&lМатериализатор&r можно использовать для преобразования &одинамического&r значения в &остатическое&r. Это можно сделать, вставив &одинамическую&r &lКарту Переменной&r в верхний слот &lМатериализатора&r. Каждая &lКарта Переменной&r, вставленная в левый слот, будет установлена значением верхней переменной на тот момент.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"&lПрокси&r позволяет добавить дополнительный уровень косвенности для ваших переменных. Например, у вас может быть логическая программа, которая требует &остатическое&r значение. Если в будущем вы захотите изменить это значение, вам придется переписать программу, если только это значение не было &опроксировано&r, в этом случае вам просто нужно обновить значение в &lПрокси&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"Для этого урока создайте материализованные и проксированные &lКарты Переменных&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"Продвинутые Операции\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"Как вы могли заметить в последнем уроке, &lПрограмматор Логики&r позволяет делать гораздо больше, чем просто складывать числа. В этом уроке мы рассмотрим более сложные структуры данных и цепочки операторов.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"Давайте начнем с создания статического &7Списка&0 из &6Целых Чисел&0 &61&0, &610&0 и &6100&0 и визуализируем его в &lДисплее&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7Предметы&0 — это один из возможных типов значений в этом моде. Давайте создадим статическую &7Предметную&0 &lКарту Переменной&r, динамически получим имя мода из этого предмета и визуализируем его в &lДисплее&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"Помните, что вам нужно использовать &lХранилище Переменных&r для хранения ваших ссылочных переменных.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"Значение Операторов\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"В качестве первого шага перейдите в &lПрограмматор Логики&r и создайте новый статический &2Оператор&0, начав вводить &oАрифметическое Сложение&r &2Оператора&0 в выпадающем списке автозаполнения и нажав на него, затем запишите его на &lКарту Переменной&r. После этого создайте две переменные типа &6Целое Число&0. Затем найдите оператор &oПрименить&r и примените первое &6Целое Число&0 к &2Оператору&0, что &oсвяжет&r первый входной аргумент оператора сложения с этим &6Целым Числом&0 и выведет новый &2Оператор&0 с одним оставшимся входным аргументом. Затем снова используйте оператор &oПрименить&r, чтобы &oсвязать&r последний входной аргумент вновь созданного &2Оператора&0. Наконец, используйте &lДисплей&r для визуализации вашего динамически вызванного сложения.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"Помимо применения &2Операторов&0, существует множество других продвинутых операций с &2Операторами&0. Теперь мы рассмотрим использование &2Операторов&0 на &7Списках&0, а именно оператор &oФильтр&r, который принимает &7Список&0 и &oПредикат&r (оператор с одним входным аргументом и выходным значением типа &9Логическое&0). Начните с создания &7Списка&0 из &6Целых Чисел&0 &61&0, &610&0 и &6100&0. Затем сохраните оператор &oРеляционное Равенство&r в переменной. Примените этот оператор к &6Целому Числу&0 &610&0. Затем используйте оператор &oФильтр&r с вашим &7Списком&0 и примененным &2Оператором&0. Это отфильтрует все элементы из &7Списка&0, которые возвращают &9Ложь&0 при применении к данному оператору, что в данном случае означает, что все элементы, не равные &610&0, будут удалены из списка. Визуализируйте результат в &lДисплее&r.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"Существует множество других операций с &2Операторами&0, таких как комбинирование &oПредикатов&r, изменение порядка аргументов &2Операторов&0 и дополнительные операции над &7Списками&0.\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"Вызовы\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"Этот финальный раздел учебника содержит несколько нетривиальных задач, чтобы показать, что вы понимаете основы &1Integrated Dynamics&0. Дополнительная информация для каждого вызова предоставляться не будет, так что удачи!\",\n    \"info_book.integrateddynamics.manual\": \"Руководство\",\n    \"info_book.integrateddynamics.manual.introduction\": \"Введение\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"Предварительные условия\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"Деревья Менрила\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"Вы можете время от времени встречать высокие синие деревья. Такие деревья называются &lДеревьями Менрила&r, и они обладают очень интересными свойствами. Во-первых, их листья излучают свет. Это вызвано светящимися ягодами, которые также очень вкусные! Во-вторых, красивая древесина этих деревьев содержит много смолы, которую можно использовать для создания большинства предметов в этом моде.\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"Биом Менеглина\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"&lДеревья Менрила&r, к сожалению, довольно сложно найти. К счастью, существует один биом — &lБиом Менеглина&r, где эти деревья процветают. Большое количество &lДеревьев Менрила&r заставляет некоторые объекты в этом биоме становиться синими, что вызвано &lСмолой Менрила&r, выделяемой этими деревьями в окружающую среду.\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"Пресс\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"Сушильный Бассейн\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"Сушка блоков и предметов может быть выполнена с помощью &lСушильного Бассейна&r. Это может быть полезно, например, если вы хотите высушить &lСмолу Менрила&r в &lКристаллизованный Менрил&r.\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"Что бы вы ни делали, не вставляйте горячие жидкости!\",\n    \"info_book.integrateddynamics.manual.networks\": \"Сети\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"&lIntegrated Dynamics&r &lсети&r являются основой любой хорошей системы автоматизации. Сеть состоит из набора соединенных &lЛогических Кабелей&r, на которые могут быть установлены различные части. Эти кабели следует управлять с помощью &lГаечного Ключа&r. Shift+правый клик по кабелю или части удалит его, а обычный правый клик отсоединит его от других кабелей.\",\n    \"info_book.integrateddynamics.manual.logic\": \"Логика\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"Типы Значений\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean\": \"Логический\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"Простейший тип значения, его значения могут быть либо &9Истина&0, либо &9Ложь&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer\": \"Целое Число\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"Тип значения для представления положительных и отрицательных чисел. Например, &6-20&0, &60&0, &61000&0, &6333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"Значения могут находиться в диапазоне от &6–2147483648&0 до &62147483647&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"Этот тип значения является частью категории &lЧисло&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double\": \"Дробное Число\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"Вещественные числа, которые также могут быть положительными или отрицательными. Например, &e3.1415&0, &e4815162342&0, &e3.33333333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"Этот тип значения является частью категории &lЧисло&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long\": \"Длинное Целое\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"То же, что и &6Целые Числа&0, но с большим диапазоном значений, который простирается от &e-9223372036854775808&0 до &e9223372036854775807&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"Этот тип значения является частью категории &lЧисло&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string\": \"Строка\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"Последовательность символов, например, &4”aaa”&0, &4“Привет!”&0, &4“x!(àà)ç”&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list\": \"Список\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator\": \"Оператор\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"Значение, содержащее ссылку на &2Оператор&0. Операторы могут быть частично применены для более гибкой оценки на основе концепции &lКаррирования&r. Это часть концепции &lметапрограммирования&r (поищите это, это реально!), где операторы рассматриваются как данные другими операторами. По сути, это позволяет вам создавать новые операторы самостоятельно, которые затем можно использовать для более сложных операций.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"Например, вы можете сохранить оператор сложения &2Оператора&0 на карте переменной. Сделайте это, начав вводить имя оператора, выбрав его из выпадающего списка автозаполнения и нажав на него. Затем вы можете частично применить его с одной &6Целочисленной&0 &lКартой Переменной&r, что приведет к созданию нового виртуального оператора с одним входным и одним выходным типом, который внутренне содержит ссылку на примененное &6Целое Число&0. Применение другого &6Целого Числа&0 даст результат в виде &6Целого Числа&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"Другие операции с &2Операторами&0 доступны в &lПрограмматоре Логики&r для более сложных вычислений, таких как свертывание списка в одно значение или фильтрация списка на основе &lПредиката&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"Определенные вещи в &lMinecraft&r внутренне используют структуру данных &3NBT&0. Тег &3NBT&0 — это особый вид объекта, который может содержать несколько значений разных типов. Например, NBT-представление &8Сундука&0, содержащего все его предметы, информация о зачарованиях &8Кирки&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"Переменные могут хранить ссылки на такие теги &3NBT&0, и существуют соответствующие операторы для чтения или расширения этих тегов.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"Теги &3NBT&0 обычно используются в этом моде как механизм низкоуровневого резервного копирования на случай, если вы не можете прочитать определенную информацию с помощью специализированных операторов.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block\": \"Блок\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"Ссылка на блок &lMinecraft&r. Например, &8Земля&0, &8Булыжник&0, &8Красная Глина&0, &8Сундук&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"Предмет\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"Ссылка на предмет &lMinecraft&r. Например, &8Яблоко&0, &8Булыжник&0, &8Алмазная Кирка&0, &8Зачарованный Меч&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"Сущность\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"Ссылка на определенную сущность &lMinecraft&r. Например, &8Зомби&0, &8Корова&0, &8Игрок_abc&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"Жидкость\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"Ссылка на жидкость &lMinecraft&r. Например, &8Лава@1000мБ&0, &8Вода@3мБ&0, &8Смола Менрила@3030мБ&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"Ингредиенты\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients.text1\": \"Коллекция &lПредметов&r, &lЖидкостей&r и &lКоличеств Энергии&r (&6Целое Число&0). Основное использование — представление входных и выходных данных &8Рецептов&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"Рецепт\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text1\": \"Абстрактный рецепт крафта, который принимает &8Ингредиенты&0 на входе и выводит другое значение &8Ингредиентов&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text2\": \"Рецепты можно создавать визуально с помощью &lПрограмматора Логики&r или считывать с (поддерживаемых) машин с помощью &lСчитывателя Машин&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text3\": \"Эти рецепты могут использоваться для автоматического крафта в модах, таких как &lIntegrated Crafting&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text4\": \"Чтобы быстро найти конкретные рецепты среди существующих &lКарт Переменных&r внутри графического интерфейса, вы можете удерживать Shift, чтобы временно отобразить выходной предмет рецепта.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text5\": \"Shift-кликом по слоту ввода внутри &lПрограмматора Логики&r вы можете настроить подробные свойства этого слота ввода. Например, вы можете указать, следует ли учитывать NBT, может ли этот предмет использоваться повторно в нескольких рецептах (например, если он не будет полностью израсходован после завершения работы), и следует ли рассматривать альтернативы на основе тегов для этого предмета.\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"Переменные\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"Карта Переменной\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&lКарты Переменных&r являются основным способом передачи логики в этом моде. Переменная содержит ссылку на определенное &lЗначение&r с определенным &lТипом Значения&r.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"У каждой &lКарты Переменной&r есть уникальный ID, который используется внутри сетей для &lЛогической Оценки&r. Когда в сети возникает ошибка, эти ID будут показаны игроку для помощи в отладке. Для лучшего удобства использования можно присвоить &lКартам Переменных&r метку с помощью &lМетчика&r, что позволит игрокам видеть метку вместо ID.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"Хранилище Переменных\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"Некоторые динамические переменные оцениваются на основе других переменных, где доступ к этим ссылочным переменным должен быть возможен. Динамическая переменная, представляющая сложение двух других переменных, должна иметь возможность получить доступ к этим двум другим переменным при её оценке. Доступ к этим другим переменным должен быть возможен в той же сети, где оценивается динамическая переменная, что можно сделать, поместив другие карты переменных в &lХранилище Переменных&r. В сети может присутствовать несколько &lХранилищ Переменных&r. Эти другие переменные также могут ссылаться на определенные переменные, которые также должны присутствовать в той же сети.\",\n    \"info_book.integrateddynamics.manual.logic.advanced\": \"Продвинутый\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path\": \"Путь NBT\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text4\": \"Можно выбрать несколько полей, предоставив массив имен полей или подстановочный знак, например, &4[fieldA,fieldB]&0 или &4*&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text6\": \"При работе со списками NBT можно выбрать конкретные элементы, указав индекс, например, &4[1]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text7\": \"Например, применение выражения &4”.a[1]”&0 к тегу &3NBT&0 &3{ a: [10,20] }&0 выведет &3”20”&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text8\": \"Можно выбрать несколько индексов списка с помощью оператора среза: &4[start:end:step]&0. (Требуется только start, остальные элементы могут быть опущены).\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text9\": \"Например, применение выражения &4”.a[1:4:2]”&0 к тегу &3NBT&0 &3{ a: [0,1,2,3,4,5] }&0 выведет &3[1,3]&0.\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text10\": \"Также возможны более сложные выражения фильтрации с использованием синтаксиса фильтров, например, &4[?(@.childName < 10)]&0 (@ ссылается на текущий тег, .. — на родительский тег, $ — на корневой тег).\",\n    \"info_book.integrateddynamics.manual.logic.advanced.nbt_path.text11\": \"Например, применение выражения &4”$.a[?(@ == 3)]”&0 к тегу &3NBT&0 &3{ a: [0,1,2,3,4,5] }&0 выведет &3[3]&0.\",\n    \"info_book.integrateddynamics.manual.parts\": \"Части\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"Введение\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text3\": \"&lЭнергетический Канал&r части определяет, откуда она может получать энергию. -1 — это универсальный канал (он соответствует всем каналам), а 0 — это канал по умолчанию (используется частями, такими как энергетические ячейки, у которых нет настройки канала). Наконец, &lЦелевая Сторона&r определяет, с какой стороны целевого блока будет происходить взаимодействие. По умолчанию это сторона, на которую фактически была размещена часть.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text4\": \"Некоторые части для чтения и записи содержат аспекты только при наличии определенных модов. Если для определенной части отсутствуют аспекты, предупреждение будет добавлено в подсказку предмета при попытке его создания.\",\n    \"info_book.integrateddynamics.manual.parts.settings\": \"Настройки\",\n    \"info_book.integrateddynamics.manual.parts.settings.text1\": \"У большинства частей есть экран &lНастроек&r, с помощью которого можно изменять базовые параметры части.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text3\": \"&lТики/Операция&r позволяют настроить частоту тиков этой части. Чем выше значение, тем медленнее она работает.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text4\": \"&lПриоритет&r определяет порядок, в котором эта часть выполняется в рамках одного тика сети.\",\n    \"info_book.integrateddynamics.manual.parts.settings.text5\": \"&lЭнергетический Канал&r указывает, из какого канала должна потребляться энергия при работе этой части. Это применимо только в том случае, если потребление энергии для сетей включено.\",\n    \"info_book.integrateddynamics.manual.parts.offsets\": \"Смещения\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text2\": \"Смещения всегда относительны к целевой позиции части и ограничены количеством примененных Улучшений Части.\",\n    \"info_book.integrateddynamics.manual.parts.offsets.text4\": \"Смещения могут быть не только статически определены, но и быть &одинамическими&r с использованием &lКарт Переменных&r.\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"Считыватели\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"Введение\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"Аудио\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"Информация о музыке и аудио, воспроизводимых в окружающей среде.\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"Блок\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"Информация о блоке, на который направлена часть.\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"Сущность\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"Информация о сущности или сущностях, находящихся внутри области, на которую направлена часть.\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"Экстра-Пространственный\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"Информация о сервере, такая как список игроков онлайн.\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"Жидкость\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"Считывание значений из резервуара жидкости, на который направлена эта часть.\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"Инвентарь\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"Считывание информации из инвентаря, на который направлена часть, например, количество слотов и предметов. Сама передача предметов не происходит.\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"Машина\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"Можно считать информацию о машине, на которую направлена часть, например, о выполняемой работе.\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"Сеть\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"Считывание информации о сети &lIntegrated Dynamics&r, на которую направлена часть. Это не обязательно должна быть та же сеть, к которой принадлежит эта часть.\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"Красный Камень\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"Считывание информации о красном камне из блока, на который направлена часть.\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"Мир\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"Информация о мире, в котором находится эта часть, например, текущая погода и время.\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"Записывающие Устройства\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"Аудио\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"Воспроизводить звуки и музыку.\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"Эффекты\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"Генерировать частицы на основе определенных значений и настроек.\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"Сущности\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"Настройка и управление сущностями.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"Машины\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"Настройка и управление машинами.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"Инвентарь\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"Выполнять действия, связанные с инвентарем и трубами. Сама передача предметов не происходит.\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"Красный Камень\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"Выводить определенный уровень сигнала красного камня.\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"Другие\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"Свет\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"Простой источник света, который либо всегда включен, либо может управляться с помощью &1Логической&0 или &6Целочисленной&0 &lКарты Переменной&r.\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"Дисплей\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"Полезный инструмент для мониторинга или отладки, который просто показывает значение &lКарты Переменной&r. &lДисплеи&r можно поворачивать правым кликом с помощью &lГаечного Ключа&r.\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text2\": \"Внутри графического интерфейса есть кнопка &lКопировать&r, которая позволяет скопировать текущее отображаемое значение в буфер обмена, чтобы его можно было использовать вне игры. Вы также можете просто использовать стандартную горячую клавишу копирования вашей ОС для копирования этого значения.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"Однонаправленный Коннектор\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"Иногда ваши логические сети будут охватывать большие расстояния. Хотя обычные кабели справятся с задачей, размещение их на больших расстояниях может быть неудобным.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&lОднонаправленные Коннекторы&r позволяют вам подключать сети беспроводным образом на большие расстояния. Это можно сделать, разместив первый коннектор в одной сети и второй коннектор в другой сети. Эти два коннектора должны находиться на прямой линии друг к другу на расстоянии не более 512 блоков. Коннекторы будут выглядеть активными, когда установлено корректное соединение.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"Омни-Направленный Коннектор\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"Хотя &lОднонаправленные&r коннекторы полезны для дальних соединений в одном направлении, в некоторых случаях это может быть слишком ограничивающим.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"Например, вы можете захотеть подключить более двух сетей с помощью одного коннектора или подключиться к сетям в других измерениях. Для таких случаев вы можете использовать &lОмни-Направленные&r коннекторы.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"Эти коннекторы получают уникальный идентификатор группы при первом создании, который определяет группу, в которой могут быть подключены сети. У каждого коннектора в группе есть уникальный цвет частиц при размещении в мире. Идентификатор группы можно увидеть в подсказке предмета или нажав правой кнопкой мыши на размещенный коннектор. Дополнительные коннекторы можно добавить в группу, создав новый с использованием существующего коннектора из группы.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"Вы также можете изменить группу существующих коннекторов двумя способами. Создание одного в верстаке назначит его новой группе без других участников, а создание двух вместе скопирует группу первого на второй.\",\n    \"info_book.integrateddynamics.manual.machines\": \"Машины\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"Материализатор\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"Чтобы сделать динамическую &lКарту Переменной&r статической, можно использовать &lМатериализатор&r. Динамическая &lКарта Переменной&r должна быть вставлена, а новая пустая &lКарта Переменной&r должна быть вставлена, что приведет к тому, что она будет содержать текущее значение динамической &lКарты Переменной&r как статическое значение.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"Это может быть полезно в ситуациях, когда вы хотите записывать значения определенной динамической &lКарты Переменной&r, или когда вам больше не нужна динамическая оценка &lКарты Переменной&r.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&lКарты Переменных&r могут быть подключены для входа и выхода из &lМатериализатора&r.\",\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"Прокси\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"&lПрокси&r можно использовать для создания динамической ссылки на определенную &lКарту Переменной&r. Он позволяет вам вставить &lКарту Переменной&r в верхний слот и записать новую &lКарту Переменной&r, которая будет динамически принимать значение &lКарты Переменной&r, присутствующей в прокси. Обратите внимание, что ссылочная (или любая другая) &lКарта Переменной&r должна оставаться в верхнем слоте для работы прокси.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"Это полезно в ситуациях, когда у вас есть цепочка &lКарт Переменных&r, но вы все же хотели бы иметь возможность изменять одно значение вручную. Вы можете тогда проксировать эту переменную, что позволит заменить её на другую переменную в любое время.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"Как и в случае с &lМатериализатором&r, &lКарты Переменных&r могут быть подключены для входа и выхода из него.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"В некоторых ситуациях вам может понадобиться поддерживать историю значений переменных. Например, если вы хотите вычислить среднее значение или разницу между последними значениями.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"Энергетическая Батарея\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"Сети &lIntegrated Dynamics&r могут быть настроены на потребление энергии. Для этого вы можете подключить &lЭнергетические Батареи&r к сети, чтобы увеличить её ёмкость для хранения энергии.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"Подача сигнала красного камня включит режим автоматического экспорта энергии на все стороны.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"Когда эта батарея находится в вашем инвентаре, режим &lавто-подачи&r можно включить, нажав Shift + правый клик. Когда этот режим включен, батарея будет автоматически пытаться заряжать предметы, которые вы держите в руках.\",\n    \"info_book.integrateddynamics.manual.machines.generator\": \"Генератор\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"Механический Пресс\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"Механический Сушильный Бассейн\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"Сушка с помощью &lСушильного Бассейна&r может занять некоторое время. Если вы хотите ускорить процесс или иметь возможность сушить больше вещей, вы можете использовать &lМеханический Сушильный Бассейн&r, который работает за счет энергии.\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"Логическое Программирование\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"Другой метод создания динамических карт переменных — это применение &lОператора&r к одной или нескольким &lКартам Переменных&r для получения новой &lКарты Переменной&r. Примером &lОператора&r является сложение двух &lЧисел&r, что приводит к получению нового &lЧисла&r.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"&lПрограмматор Логики&r содержит большой набор &lОператоров&r. Он также позволяет создавать статические &lКарты Переменных&r для всех &lТипов Значений&r. После выбора &lОператора&r, в соответствующие входные слоты должны быть вставлены допустимые &lКарты Переменных&r. Наконец, другая &lКарта Переменной&r должна быть вставлена в выходной слот. Если &lОператор&r действителен, на карту будет записана новая динамическая переменная. Помните, что связанные &lКарты Переменных&r должны присутствовать в &lХранилище Переменных&r в той же сети, где будет оцениваться &lОператор&r.\",\n    \"info_book.integrateddynamics.manual.diagnostics\": \"Диагностика Сетей\",\n    \"info_book.integrateddynamics.manual.diagnostics.text1\": \"Игроки с правами оператора имеют доступ к инструменту &lДиагностика Сетей&r, который позволяет проверить время тиков для всех сетей на сервере.\",\n    \"info_book.integrateddynamics.manual.diagnostics.text2\": \"Чтобы включить этот инструмент, выполните команду &o\\\\integrateddynamics networkdiagnostics&r, которая запустит временный локальный веб-сервер.\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/sr_sp.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/sv_se.json",
    "content": "{\n    \"general.integrateddynamics.description\": \"Få fullständig och automatiserad kontroll över dina redskap.\",\n    \"general.integrateddynamics.info.none\": \"Ingenting\",\n    \"general.integrateddynamics.error.no_network\": \"Hittade inget giltigt nätverk\",\n    \"general.integrateddynamics.true\": \"Sant\",\n    \"general.integrateddynamics.false\": \"Falskt\",\n    \"gui.integrateddynamics.button.save\": \"Spara\",\n    \"gui.integrateddynamics.button.rename\": \"Byt namn\",\n    \"gui.integrateddynamics.button.copy\": \"Kopiera\",\n    \"gui.integrateddynamics.button.copy.info\": \"Kopierar till urklipp\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"Tick/operation\",\n    \"gui.integrateddynamics.partsettings.priority\": \"Prioritet\",\n    \"gui.integrateddynamics.partsettings.channel\": \"Energikanal\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"Konsumtion av nätverksenergi är inaktiverad på denna server.\",\n    \"gui.integrateddynamics.partsettings.side\": \"Målsida\",\n    \"gui.integrateddynamics.delay.capacity\": \"Kapacitet\",\n    \"gui.integrateddynamics.part.properties\": \"Egenskaper:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"Filtrera\",\n    \"gui.integrateddynamics.rewards\": \"Belöningar\",\n    \"gui.integrateddynamics.rewards.collect\": \"Samla in\",\n    \"gui.integrateddynamics.rewards.collected\": \"Har redan samlats in\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"Kan inte samla in belöningar. Är din utrustning full?\",\n    \"gui.integrateddynamics.input\": \"Inmatning: %s\",\n    \"gui.integrateddynamics.output\": \"Utmatning: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"Tryck ut vätska automatiskt: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"Trycker till närliggande tankar.\",\n    \"gui.integrateddynamics.input_short\": \"Inmat.\",\n    \"gui.integrateddynamics.output_short\": \"Utmat.\",\n    \"gui.integrateddynamics.diagnostics.title\": \"Nätverksdiagnostik\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"Delar\",\n    \"gui.integrateddynamics.diagnostics.observers\": \"Lagringsobservatörer\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"Nätverk\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"Kablar\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"Del\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"Ticktid (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"Dim\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"sida\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"Gå till sökfältet i logikprogr.\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"Döp om variabel i logikprogr.\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"Sida vänds\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"Sidor vänds\",\n    \"block.integrateddynamics.cable\": \"Logisk kabel\",\n    \"block.integrateddynamics.variablestore\": \"Variabellagring\",\n    \"block.integrateddynamics.energy_battery\": \"Energibatteri\",\n    \"block.integrateddynamics.energy_battery.info\": \"Placera i tillverkningsrutan med andra energibatterier för att öka kapaciteten. Shift+högerklicka för att aktivera autofyll.\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"Autofyll: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"Kreativt energibatteri\",\n    \"block.integrateddynamics.logic_programmer\": \"Logikprogrammerare\",\n    \"block.integrateddynamics.coal_generator.info\": \"Producerar energi från bränsleföremål\",\n    \"block.integrateddynamics.proxy.info\": \"Exponerar en variabelreferens som en aspekt. Kan rensas i en tillverkningsruta.\",\n    \"block.integrateddynamics.materializer\": \"Materialiserare\",\n    \"block.integrateddynamics.materializer.info\": \"Materialiserar en variabels värde i en ny variabel\",\n    \"block.integrateddynamics.menril_log\": \"Menrilträ\",\n    \"block.integrateddynamics.menril_log_filled\": \"Berikat menrilträ\",\n    \"block.integrateddynamics.menril_leaves\": \"Menrillöv\",\n    \"block.integrateddynamics.menril_sapling\": \"Menrilplanta\",\n    \"block.integrateddynamics.menril_planks\": \"Menrilträplankor\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"Kristalliserat menrilblock\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"Kristalliserat menriltegel\",\n    \"block.integrateddynamics.block_menril_resin\": \"Menrilkåda\",\n    \"block.integrateddynamics.drying_basin\": \"Torkningskar\",\n    \"block.integrateddynamics.drying_basin.info\": \"Torkar föremål och vätskor\",\n    \"block.integrateddynamics.squeezer\": \"Pressare\",\n    \"block.integrateddynamics.squeezer.info\": \"Pressar, plattar till och krossar genom att hoppa på den. Återställs med en redstonepuls.\",\n    \"block.integrateddynamics.menril_torch\": \"Menrilfackla\",\n    \"block.integrateddynamics.menril_torch_stone\": \"Menrilstenfackla\",\n    \"block.integrateddynamics.invisible_light\": \"Osynligt ljus\",\n    \"block.integrateddynamics.menril_door\": \"Menrildörr\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"Menrilträtrappa\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"Kristalliserad menriltrappa\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"Kristalliserad menriltegeltrappa\",\n    \"block.integrateddynamics.delay\": \"Fördröjare\",\n    \"block.integrateddynamics.delay.info\": \"Exponerar historiken för en variabel som en aspekt. Kan rensas i en tillverkningsruta.\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"Flytande körfrukt\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"Kristalliserat körblock\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"Kristalliserat körtegel\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"Kristalliserad körtrappa\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"Kristalliserad körtegeltrappa\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"Mekanisk pressare\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"Pressar, plattar till och krossar med energi.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"Mekaniskt torkningskar\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"Torkar föremål och vätskor med energi.\",\n    \"item.integrateddynamics.wrench\": \"Blocknyckel\",\n    \"item.integrateddynamics.variable\": \"Variabelkort\",\n    \"item.integrateddynamics.variable.info\": \"Kan rensas eller kopieras i en tillverkningsruta\",\n    \"item.integrateddynamics.variable.warning\": \"§4§lVARNING: Kopiera INTE detta föremål med mittenklick!\",\n    \"item.integrateddynamics.variable.id\": \"§e§oVariabel-ID: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"Etikettskrivare\",\n    \"item.integrateddynamics.labeller.info\": \"Märker dina variabler med en etikett. Fungerar även i logikprogrammerare.\",\n    \"item.integrateddynamics.labeller.button.write\": \"Skriv\",\n    \"item.integrateddynamics.facade\": \"Fasad\",\n    \"item.integrateddynamics.facade.info\": \"Kombineras med ett block i en arbetsbänk\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"Hink med menrilkåda\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"Kristalliserad menrilskärva\",\n    \"item.integrateddynamics.variable_transformer\": \"Variabelomvandlare för in-/utmatning\",\n    \"item.integrateddynamics.menril_berries\": \"Menrilbär\",\n    \"item.integrateddynamics.menril_berries.info\": \"Välsmakande och självlysande\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"Portabel logikprogrammerare\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"Om integrerade dynamiker\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"En komplett modreferensguide med handledning\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"Kristalliserad körskärva\",\n    \"item.integrateddynamics.logic_director\": \"Logikriktare\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"Meneglinsk upptäckt\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"Hugg ned ett menrilträd, som man lättast kan hitta i meneglinbiomer.\",\n    \"advancement.integrateddynamics.squeezing\": \"Pressa som ett proffs\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"Tillverka en pressare.\",\n    \"advancement.integrateddynamics.drying\": \"Torka som ett proffs\",\n    \"advancement.integrateddynamics.drying.desc\": \"Tillverka ett torkningskar.\",\n    \"advancement.integrateddynamics.menril_production\": \"Menrilproduktion\",\n    \"advancement.integrateddynamics.cables_logic\": \"Logiska kablar\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"Tillverka en logisk kabel\",\n    \"advancement.integrateddynamics.networks_logic\": \"Logiska nätverk\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"Bygg ett logiskt nätverk med minst 10 anslutna logiska kablar.\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"Menrilverktyg\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"Tillverka en blocknyckel\",\n    \"advancement.integrateddynamics.variables\": \"Variabla värden\",\n    \"advancement.integrateddynamics.variables.desc\": \"Tillverka ett variabelkort\",\n    \"advancement.integrateddynamics.variable_input\": \"Variabelomvandlad inmatning\",\n    \"advancement.integrateddynamics.variable_output\": \"Variabelomvandlad utmatning\",\n    \"advancement.integrateddynamics.redstone_reading\": \"Redstoneavläsning\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"Tillverka en redstoneläsare\",\n    \"advancement.integrateddynamics.block_reading\": \"Blockläsning\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"Tillverka en blockläsare\",\n    \"advancement.integrateddynamics.inventory_reading\": \"Utrustningsavläsning\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"Tillverka en utrustningsläsare\",\n    \"advancement.integrateddynamics.machine_reading\": \"Maskinläsning\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"Tillverka en maskinläsare\",\n    \"advancement.integrateddynamics.value_displaying\": \"Värdeuppvisning\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"Tillverka en skärmpanel\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"Redstonefångst\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"Skapa ett variabelkort för ett redstonevärde av typen §6heltal§7.\",\n    \"advancement.integrateddynamics.redstone_observement\": \"Redstoneobservation\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"Placera ett variabelkort av typen §6heltal§7 i en skärmpanel.\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"Redstoneöverföring\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"Låt en redstoneskrivare skriva en maximal redstonenivå med ett heltal.\",\n    \"advancement.integrateddynamics.recipe_reading\": \"Receptläsning\",\n    \"advancement.integrateddynamics.recipe_reading.desc\": \"Läs listan över recept från en maskin (som arbetsbänken) med en maskinläsare.\",\n    \"advancement.integrateddynamics.recipe_creation\": \"Receptskapare\",\n    \"advancement.integrateddynamics.recipe_creation.desc\": \"Skapa visuellt ett §8recept§7 med logikprogrammeraren.\",\n    \"advancement.integrateddynamics.recipe_lookup\": \"Receptgranskning\",\n    \"advancement.integrateddynamics.recipe_lookup.desc\": \"Slå dynamiskt upp receptet för en kista med en maskinläsare.\",\n    \"advancement.integrateddynamics.logic_programming\": \"Logisk programmering\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"Öppna gränssnittet på en logikprogrammerare\",\n    \"advancement.integrateddynamics.constant_definition\": \"Konstant definition\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"Definiera ett variabelkort med ett konstant §6heltal§7.\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"Aritmetisk addition\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"Lägg ihop två §esiffror§7 och visa resultatet.\",\n    \"advancement.integrateddynamics.variable_materialization\": \"Variabelmaterialisering\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"Materialisera en variabel i en §lmaterialiserare§r.\",\n    \"advancement.integrateddynamics.variable_proxying\": \"Proxyvariabler\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"Skapa en giltig proxyvariabel i en §lproxy§r.\",\n    \"advancement.integrateddynamics.logical_list_building\": \"Logiskt listbyggande\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"Bygg en statisk lista med §6heltalen§7 1, 10 och 100.\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"Identifierat föremålsursprung\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"Hämta namnet på en mod för ett föremål.\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"Vad tittar jag på?\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"Skaffa blocket du tittar på.\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"Dynamiska additioner\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"Anropa additionsoperatorn dynamiskt\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"Dynamisk listfiltrering\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"Filtrera en heltalslista med 1, 10 och 100 till 10.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"Creepertämjare\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"Visa HP för en creeper på en skärmpanel\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"Svampiga fotsteg\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"Spela upp ljudet av fotsteg på ett vått svampblock\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"Rekursiv rekursion\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"Skapa en oändlig lista och visa den på en skärmpanel\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"Verktyg för obsidian?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"Kolla om en stenhacka kan bryta obsidianblock och visa resultatet på en skärmpanel.\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"Smart tryckplatta\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"Skapa ett system som automatiskt öppnar en dörr när du går förbi en entitetsläsare som baseras på matchande entitetsnamn med en redstoneskrivare som pekar direkt mot dörren.\",\n    \"valuetype.integrateddynamics.error.value\": \"Ogiltigt värde\",\n    \"valuetype.integrateddynamics.value_type\": \"Värdetyp\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§eTyp: §r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§oVärde: §r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§eSignatur: §r%s\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"Inmatningen \\\"%s\\\" kunde inte tolkas för denna värdetyp.\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"Denna värdetyp kräver ett inmatningsföremål.\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"Elementet %s är ogiltigt: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"Listan förväntar värdetypen %s men %s var angivet.\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"En operatorsvärdetyp förväntades vid tecknet %s, men %s var angivet\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"Operatorvärdet %s förväntade värdetyper (%s), men (%s) var angivet.\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"Det infogade föremålet är inte ett block.\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"Det infogade föremålet innehåller inte någon vätska.\",\n    \"valuetype.integrateddynamics.any\": \"Alla\",\n    \"valuetype.integrateddynamics.any.info\": \"Accepterar alla värdetyper.\",\n    \"valuetype.integrateddynamics.number\": \"Siffra\",\n    \"valuetype.integrateddynamics.number.info\": \"Accepterar alla siffertyper.\",\n    \"valuetype.integrateddynamics.named\": \"Till sträng\",\n    \"valuetype.integrateddynamics.named.info\": \"Omvandlar ett värde till en strängrepresentation\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"Till unikt namn\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"Omvandlar ett värde till dess unika namn\",\n    \"valuetype.integrateddynamics.boolean\": \"Boolesk\",\n    \"valuetype.integrateddynamics.integer\": \"Heltal\",\n    \"valuetype.integrateddynamics.string\": \"Sträng\",\n    \"valuetype.integrateddynamics.list\": \"Lista\",\n    \"valuetype.integrateddynamics.itemstack\": \"Föremål\",\n    \"valuetype.integrateddynamics.entity\": \"Entitet\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Vätska\",\n    \"valuetype.integrateddynamics.ingredients\": \"Ingredienser\",\n    \"valuetype.integrateddynamics.recipe\": \"Recept\",\n    \"parttype.integrateddynamics.error.low_energy\": \"Inte tillräckligt mycket energi\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"Slut på energi\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"Inaktiv\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"Fel:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"Aspekt: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"Värde: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"Grupp: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"VARNING: Inga tillgängliga aspekter. Detta kan bli oanvändbart när andra moddar installeras.\",\n    \"parttype.integrateddynamics.audio_reader\": \"Ljudläsare\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"Läser ljudinformation\",\n    \"parttype.integrateddynamics.block_reader\": \"Blockläsare\",\n    \"parttype.integrateddynamics.block_reader.info\": \"Läser blockinformation\",\n    \"parttype.integrateddynamics.entity_reader\": \"Entitetsläsare\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"Läser entitetsinformation\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"Extradimensionell läsare\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"Läser information som delas för alla dimensioner\",\n    \"parttype.integrateddynamics.fluid_reader\": \"Vätskeläsare\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"Läser vätsketankar\",\n    \"parttype.integrateddynamics.inventory_reader\": \"Utrustningsläsare\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"Läser data från utrustningar\",\n    \"parttype.integrateddynamics.network_reader\": \"Nätverksläsare\",\n    \"parttype.integrateddynamics.network_reader.info\": \"Läser egenskaper om ett målnätverk\",\n    \"parttype.integrateddynamics.machine_reader\": \"Maskinläsare\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"Läser maskinegenskaper\",\n    \"parttype.integrateddynamics.redstone_reader\": \"Redstoneläsare\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"Läser redstonevärden\",\n    \"parttype.integrateddynamics.world_reader\": \"Världsläsare\",\n    \"parttype.integrateddynamics.world_reader.info\": \"Läser saker som relateras till världen, block och entiteter\",\n    \"parttype.integrateddynamics.audio_writer\": \"Ljudskrivare\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"Matar ut ljud och musik\",\n    \"parttype.integrateddynamics.effect_writer\": \"Effektskrivare\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"Matar ut partiklar\",\n    \"parttype.integrateddynamics.entity_writer\": \"Entitetsskrivare\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"Skriver entitetskonfiguration\",\n    \"parttype.integrateddynamics.machine_writer\": \"Maskinskrivare\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"Skriver maskinkonfiguration\",\n    \"parttype.integrateddynamics.inventory_writer\": \"Utrustningsskrivare\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"Skriver till utrustningar\",\n    \"parttype.integrateddynamics.redstone_writer\": \"Redstoneskrivare\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"Skriver till redstone\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"Denna panel accepterar inte %s.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"Statisk ljuspanel\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"Dynamisk ljuspanel\",\n    \"parttype.integrateddynamics.display_panel\": \"Skärmpanel\",\n    \"parttype.integrateddynamics.display_panel.info\": \"Visar visuellt ett variabelvärde i världen som text\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"Enriktad anslutare\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"Kan anslutas till andra enriktade anslutare över en sträcka i en rak linje\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"Flerriktad anslutare\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"Kan anslutas till andra flerriktade anslutare var som helst, t.o.m. i olika dimensioner.\",\n    \"variable.integrateddynamics.error.invalid_item\": \"Nuvarande variabel kan inte användas i denna aspekt.\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"Del med ID %s kunde inte hittas inom nuvarande nätverk.\",\n    \"variable.integrateddynamics.error.recursion\": \"Variabeln med ID %s hänvisar till sig själv.\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§oProxy-ID: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"Proxy med ID %s kunde inte hittas inom det nuvarande nätverket.\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§oFördröjnings-ID: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"Fördröjning med ID %s kunde inte hittas inom det nuvarande nätverket.\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"Fördröjning med ID %s exponerar inte en variabel.\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"Fördröjning med ID %s förväntas exponera en variabel av typen %s, men %s hittades.\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§eAspekt: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§eTyp: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§oDel-ID: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"Denna aspekt förväntar %s, men %s var angivet.\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"Virveltrumsnot\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"Läser en virveltrumsnot, förväntat intervall [0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"Virveltrumsnot\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"Matar ut en virveltrumsnot, förväntat intervall [0, 24]\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"Ljud\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"Spelar upp angiven ljudnamn\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"Har block\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"Om målrymden har ett block\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"X-koordinat\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"Målets X-koordinat\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Y-koordinat\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"Målets Y-koordinat\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Z-koordinat\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"Målets Z-koordinat\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"Blockentitetens NBT\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"Målblockentiteten som NBT\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"Målblocket\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"Rotation för föremålram\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"Rotationsindex för föremålet i föremålsramen.\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"Entiteter\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"Entiteterna i målrymden\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"Spelare\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"Spelarna som är i denna värld\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"Entitet\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"Den markerade entiteten i målrymden\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"Föremålsram\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"Föremålet i en föremålsram som är fäst på läsaren\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"Slump\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"Ett slumpartat heltalsvärde\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"Spelarantal\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"Antalet spelare på servern\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"Ticktid\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"Medelticktiden i nanosekunder för servern\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"Medelantalet tick per sekunder över alla världar\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"Spelare\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"Spelarna som är anslutna på denna server\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"Tom tank\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"Om målvätsketanken är tom\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"Full tank\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"Om målvätsketanken är full\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"Tank är inte tom\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"Om målvätsketanken har någonting i sig\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"Är tank\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"Om målet har en vätsketank\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"Tankar\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"Antalet tankar som finns på målet\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"Vätskefyllningsförhållande\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"Mängden vätska i den aktiva tanken dividerat med dess kapacitet\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"Tankvätskor\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"Listan över vätskor i alla tankar\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"Tankkapaciteter\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"Listan över kapaciteter för alla tankar\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"Tankvätska\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"Vätskan i måltanken\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"Blockvätska\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"Vätskan i målblocket\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"Full utrustning\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"Om alla platser har minst ett föremål\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"Tom utrustning\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"Om alla platser är tomma\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"Utrustningen är inte tom\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"Om det finns minst ett föremål\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"Har utrustning\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"Om målet har en utrustning\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"Utrustningsantal\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"Hämtar det totala antalet föremål\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"Föremålsgruppstorlek för plats\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"Hämtar storleken på föremålsgruppen i den aktiva platsen\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"Platser\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"Antalet platser i utrustningen\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"Fyllda platser\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"Antalet platser som har ett föremål\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"Fyllningsförhållande\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"Antalet ofyllda platser dividerat med totala antalet platser\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"Föremål\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"Hämtar en lista över föremålen i utrustningen\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"Platsföremål\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"Hämtar föremålet i den aktiva platsen\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"Är arbetare\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"Om målet är en arbetsmaskin\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"Har arbete\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"Om målet har arbete att bearbeta\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"Kan arbeta\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"Om målet skulle kunna börja arbeta i sitt nuvarande tillstånd\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"Arbetar\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"Om målet arbetar för tillfället\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"Har temperatur\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"Om målet har en särskild temperatur\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"Temperatur\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"Maximal temperatur\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"Minimal temperatur\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"Standardtemperatur\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable\": \"Är recepthanterare\",\n    \"aspect.integrateddynamics.read.boolean.recipehandler.applicable.info\": \"Om målet kan hantera recept\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes\": \"Recept\",\n    \"aspect.integrateddynamics.read.list.recipehandler.recipes.info\": \"Recept som målet kan hantera\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput\": \"Receptutmatning efter inmatning\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeoutputbyinput.info\": \"En operator som returnerar resulterande receptutmatning för en angiven inmatning.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput\": \"Receptinmatning efter utmatning\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipeinputsbyoutput.info\": \"En operator som returnerar alla möjliga receptinmatningar för en angiven utmatning.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput\": \"Recept efter inmatning\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyinput.info\": \"En operator som returnerar alla möjliga recept för en angiven inmatning.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput\": \"Recept efter utmatning\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipesbyoutput.info\": \"En operator som returnerar alla möjliga recept för en angiven utmatning.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput\": \"Recept efter inmatning\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyinput.info\": \"En operator som returnerar ett recept för en angiven inmatning.\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput\": \"Recept efter utmatning\",\n    \"aspect.integrateddynamics.read.operator.recipehandler.recipebyoutput.info\": \"En operator som returnerar ett recept för en angiven utmatning.\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"Är nätverk\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"Om målet har ett nätverk\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"Element\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount.info\": \"Antalet element i målnätverket\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"Energibatterier\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount.info\": \"Antalet energibatterier i målnätverket\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"Energikonsumtionshastighet\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate.info\": \"Den nuvarande hastigheten på energikonsumtionen för målnätverket per tick\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"Värde\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"Värdet som exponeras av målet\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"Max redstonenivå\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"Om redstonenivån ligger på maximum\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"Ingen redstonenivå\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"Om det inte finns någon redstonenivå\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"Redstonenivå finns\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"Om det finns en redstonenivå\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"Redstoneklocka\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"Pulserar med en längd och ett intervall som kan konfigureras\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"Redstonenivå\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"Hämtar den exakta redstonenivån\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"Jämförare\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"Hämtar värdet för jämföraren\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"Matar ut en full redstonesignal om värdet är sant\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"Redstonepuls\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"Matar ut en puls med det konfigurerade redstonevärdet om det angivna värdet ändras.\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"Matar ut värdet som den exakta redstonesignalen\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"Redstonepuls\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"Matar ut en puls med det konfigurerade redstonevärdet om det angivna värdet ändras.\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"Har entitet\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"Om målrymden har en entitet\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"Har monster\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"Om målrymden har ett monster\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"Har djur\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"Om målrymden har ett djur\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"Har spelare\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"Om målrymden har en spelare\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"Har föremål\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"Om målrymden har ett föremål\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"Väder: Klart\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"Om det är klart väder i världen\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"Väder: Regn\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"Väder: Åska\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"Om det åskar i världen\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"Är dag\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"Om det är dag i målvärlden\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"Är natt\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"Om det är natt i målvärlden\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"Entiteter\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"Mängden entiteter i målrymden\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"Nedräkning för regn\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"Mängden tick tills det regnar i denna värld\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"Ticktid\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"Medelticktiden i millisekunder för denna värld\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"Dagtid\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"Tiden som har gått i målvärldens dag\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"Ljusnivå\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"Ljusnivån för målet\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"Spelarantal\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"Spelarna som är anslutna på denna värld\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"Antalet tick per sekund i denna värld\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"Tid\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"Världstiden som är beroende av kommandot \\\"time\\\"\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"Totaltid\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"Världstiden som är oberoende av kommandot \\\"time\\\"\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"Världsnamn\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"Namnet på målvärlden\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"Spelare\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"Spelarna som finns i denna värld\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"Tank-ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"Plats-ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"Listelement\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"Intervall\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"Kanal\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"Pulsintervall\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"Pulslängd\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"Förskjutning för pulstid\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"Volym\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"Frekvens\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"X-förskjutning\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Y-förskjutning\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Z-förskjutning\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"Partikelantal\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"X-spridning\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Y-spridning\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Z-spridning\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"Stark styrka\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"Om en redstonefackla bör simuleras istället för vanligt redstonedamm\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"Pulsvärde\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"Pulsvärdet som bör uteslutas.\",\n    \"operator.integrateddynamics.applied.operator_name\": \"Tillämpade %s [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"Tillämpades till: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§eKategori: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§eVariabel-ID:n: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§eInmatningstyp %s: §r%s\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§eUtmatningstyp: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"Operatorn %s tog emot en inmatning med längden %s, men det behövs en längd på %s.\",\n    \"operator.integrateddynamics.error.null_type\": \"Operatorn %s tog emot en inmatning med en null-variabel vid tecknet %s.\",\n    \"operator.integrateddynamics.error.wrong_type\": \"Operatorn %s tog emot en inmatning av typen %s vid tecknet %s, men typen %s förväntades.\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"Operatorn %s tog emot en inmatning av typen %s vid tecknet %s men typen %s förväntades för currying.\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"Operatorn %s tog emot en inmatningsoperator som matar ut typen %s medan typen %s förväntades.\",\n    \"operator.integrateddynamics.error.illegal_property\": \"Egenskapen förväntas mata ut %s, men %s hittades för operatorn %s.\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"Variabeln med ID %s kunde inte hittas inom nuvarande nätverk.\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"Variabeln med ID %s är beroende av sig själv, oändlig upprepning.\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"Operatorn %s tog emot operatorn %s med en inmatningslängd på %s men längden %s krävs.\",\n    \"operator.integrateddynamics.logical\": \"Logisk\",\n    \"operator.integrateddynamics.logical.basename\": \"%s (logisk)\",\n    \"operator.integrateddynamics.logical.and\": \"Och\",\n    \"operator.integrateddynamics.logical.or\": \"Eller\",\n    \"operator.integrateddynamics.logical.not\": \"Inte\",\n    \"operator.integrateddynamics.logical.nand\": \"Inte och\",\n    \"operator.integrateddynamics.logical.nor\": \"Inte eller\",\n    \"operator.integrateddynamics.arithmetic\": \"Aritmetisk\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"%s (aritmetisk)\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"Subtraktion\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"Multiplikation\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"Maximalt\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"Tar det största utav två värden.\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"Minimalt\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"Tar det minsta utav två värden.\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"Modulär\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"Ökning\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"Minskning\",\n    \"operator.integrateddynamics.integer\": \"Heltal\",\n    \"operator.integrateddynamics.integer.basename\": \"%s (heltal)\",\n    \"operator.integrateddynamics.integer.modulus\": \"Modulär\",\n    \"operator.integrateddynamics.integer.increment\": \"Ökning\",\n    \"operator.integrateddynamics.integer.decrement\": \"Minskning\",\n    \"operator.integrateddynamics.relational\": \"Relationell\",\n    \"operator.integrateddynamics.relational.basename\": \"%s (relationell)\",\n    \"operator.integrateddynamics.relational.equals\": \"Lika med\",\n    \"operator.integrateddynamics.relational.gt\": \"Större än\",\n    \"operator.integrateddynamics.relational.lt\": \"Mindre än\",\n    \"operator.integrateddynamics.relational.notequals\": \"Inte lika med\",\n    \"operator.integrateddynamics.relational.ge\": \"Större eller lika med\",\n    \"operator.integrateddynamics.relational.le\": \"Mindre eller lika med\",\n    \"operator.integrateddynamics.binary\": \"Binärt\",\n    \"operator.integrateddynamics.binary.basename\": \"%s (binärt)\",\n    \"operator.integrateddynamics.binary.and\": \"Och\",\n    \"operator.integrateddynamics.binary.or\": \"Eller\",\n    \"operator.integrateddynamics.binary.xor\": \"Exklusivt eller\",\n    \"operator.integrateddynamics.binary.complement\": \"Komplement\",\n    \"operator.integrateddynamics.binary.rshift\": \"Högerförskjutning\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"Förskjutning åt höger med ettor\",\n    \"operator.integrateddynamics.binary.lshift\": \"Vänsterförskjutning\",\n    \"operator.integrateddynamics.binary.rzshift\": \"Nollförskjutning åt höger\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"Förskjutning åt höger med nollor\",\n    \"operator.integrateddynamics.string\": \"Sträng\",\n    \"operator.integrateddynamics.string.basename\": \"%s (sträng)\",\n    \"operator.integrateddynamics.string.length\": \"Längd\",\n    \"operator.integrateddynamics.string.length.info\": \"Längden på den angivna strängen\",\n    \"operator.integrateddynamics.string.concat\": \"Sammanfoga\",\n    \"operator.integrateddynamics.string.concat.info\": \"Sammanfogar två angivna strängen till en sträng\",\n    \"operator.integrateddynamics.string.contains\": \"Innehåller sträng\",\n    \"operator.integrateddynamics.string.contains.info\": \"Om den angivna understrängen finns inom den angivna strängen.\",\n    \"operator.integrateddynamics.string.contains_regex\": \"Innehåller regex\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"Om det angivna reguljära uttrycket matchar någon understräng i den angivna strängen.\",\n    \"operator.integrateddynamics.string.matches_regex\": \"Matchar regex\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"Om det angivna reguljära uttrycket matchar den angivna strängen.\",\n    \"operator.integrateddynamics.string.index_of\": \"Index för\",\n    \"operator.integrateddynamics.string.index_of.info\": \"Hämtar index för den första förekomsten av en understräng som matchar söktermen i den angivna strängen.\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"Index för regex\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"Hämtar index för den första förekomsten av en understräng som matchar mönstret i den angivna strängen.\",\n    \"operator.integrateddynamics.string.starts_with\": \"Börjar med\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"Om den angivna understrängen matchar början på den angivna strängen.\",\n    \"operator.integrateddynamics.string.ends_with\": \"Slutar med\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"Om den angivna strängen matchar slutet på den angivna strängen.\",\n    \"operator.integrateddynamics.string.split_on\": \"Dela vid\",\n    \"operator.integrateddynamics.string.split_on.info\": \"Hämtar en lista som innehåller delar, som är uppdelade vid den angivna avgränsaren, av den angivna strängen.\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"Dela vid regex\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"Hämtar en lista som innehåller delar, som har delats upp med det angivna reguljära uttrycket, av den angivna strängen.\",\n    \"operator.integrateddynamics.string.substring\": \"Understräng\",\n    \"operator.integrateddynamics.string.substring.info\": \"Hämtar en understräng, från det angivna indexet (inkluderas) till det angivna indexet (exkluderas), av den angivna strängen.\",\n    \"operator.integrateddynamics.string.regex_group\": \"Regex-grupp\",\n    \"operator.integrateddynamics.string.regex_group.info\": \"Söker efter det angivna reguljära uttrycket och returnera gruppen i denna matchning vid angivet index i den angivna strängen.\",\n    \"operator.integrateddynamics.string.regex_groups\": \"Regex-grupper\",\n    \"operator.integrateddynamics.string.regex_groups.info\": \"Söker efter det angivna reguljära uttrycket och returnera en lista över alla dess grupper i denna matchning i den angivna strängen.\",\n    \"operator.integrateddynamics.string.regex_scan\": \"Sök efter regex\",\n    \"operator.integrateddynamics.string.regex_scan.info\": \"Söker efter alla matchningar för det angivna reguljära uttrycket och returnera gruppen vid angivet index i den angivna strängen.\",\n    \"operator.integrateddynamics.string.replace\": \"Ersätt\",\n    \"operator.integrateddynamics.string.replace.info\": \"Hittar alla instanser av söktermen och ersätt dem med den angivna strängen i den angivna strängen.\",\n    \"operator.integrateddynamics.string.replace_regex\": \"Ersätt regex\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"Hittar alla matchningar av sökmönstret och ersätt dem med den angivna strängen i den angivna strängen.\",\n    \"operator.integrateddynamics.string.name\": \"Namn\",\n    \"operator.integrateddynamics.string.name.info\": \"Hämtar namnet på ett namngivet objekt eller omvandlar det till e sträng\",\n    \"operator.integrateddynamics.string.unique_name\": \"Unikt namn\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"Hämtar det unika namnet för ett objekt\",\n    \"operator.integrateddynamics.double.basename\": \"%s (double)\",\n    \"operator.integrateddynamics.list\": \"Lista\",\n    \"operator.integrateddynamics.list.basename\": \"%s (lista)\",\n    \"operator.integrateddynamics.list.length\": \"Längd\",\n    \"operator.integrateddynamics.list.length.info\": \"Längden på den angivna listan\",\n    \"operator.integrateddynamics.list.empty\": \"Tom\",\n    \"operator.integrateddynamics.list.empty.info\": \"Om listan är tom\",\n    \"operator.integrateddynamics.list.notempty\": \"Inte tom\",\n    \"operator.integrateddynamics.list.notempty.info\": \"Om listan inte är tom\",\n    \"operator.integrateddynamics.list.get\": \"Listelement\",\n    \"operator.integrateddynamics.list.get.info\": \"Hämtar listelementen vid vald position, kastar ett felmeddelande om index ligger utanför listan.\",\n    \"operator.integrateddynamics.list.get_or_default\": \"Listelement eller standardvärde\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"Hämtar listelementen vid vald position på ett säkert sätt, om detta element inte är tillgängligt returneras det angivna standardvärdet.\",\n    \"operator.integrateddynamics.list.contains\": \"Innehåller\",\n    \"operator.integrateddynamics.list.contains.info\": \"Om listan innehåller angivet element.\",\n    \"operator.integrateddynamics.list.contains_p\": \"Innehåller predikat\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"Om minst ett element i listan returnerar sant för det angivna predikatet.\",\n    \"operator.integrateddynamics.list.count\": \"Antal\",\n    \"operator.integrateddynamics.list.count.info\": \"Antalet gånger det angivna elementet finns i listan.\",\n    \"operator.integrateddynamics.list.count_p\": \"Antal predikat\",\n    \"operator.integrateddynamics.list.count_p.info\": \"Antalet gånger det angivna predikatet returnerar sant för elementen i listan.\",\n    \"operator.integrateddynamics.list.append\": \"Lägg till i slutet\",\n    \"operator.integrateddynamics.list.append.info\": \"Lägg till i slutet på den angivna listan.\",\n    \"operator.integrateddynamics.list.concat\": \"Sammanfoga\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"Lat listbyggare\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"Bygger en lista med ett startvärde och en operator som läggs till i det föregående elementet för att hämta ett nästa element.\",\n    \"operator.integrateddynamics.list.head\": \"Första elementet\",\n    \"operator.integrateddynamics.list.head.info\": \"Hämtar det första element i en angiven lista.\",\n    \"operator.integrateddynamics.list.tail\": \"Ta bort sista elementet\",\n    \"operator.integrateddynamics.list.tail.info\": \"Tar bort det sista elementet från listan.\",\n    \"operator.integrateddynamics.list.uniq_p\": \"Unikt predikat\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"Tar bort alla dubblettelement i listan baserat på det angivna jämförelsepredikatet.\",\n    \"operator.integrateddynamics.list.uniq\": \"Unikt\",\n    \"operator.integrateddynamics.list.uniq.info\": \"Tar bort alla dubblettelement i listan.\",\n    \"operator.integrateddynamics.list.slice\": \"Dela\",\n    \"operator.integrateddynamics.list.slice.info\": \"Tar en underuppsättning av den angivna listan från angivet index (inkluderas) till angivet index (exkluderas).\",\n    \"operator.integrateddynamics.block.basename\": \"%s (block)\",\n    \"operator.integrateddynamics.block.opaque\": \"Opak\",\n    \"operator.integrateddynamics.block.opaque.info\": \"Om blocket är opakt, inte genomskinligt\",\n    \"operator.integrateddynamics.block.itemstack\": \"Föremål\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"Föremålsrepresentationen av det angivna blocket\",\n    \"operator.integrateddynamics.block.mod.info\": \"Namnet på modden som äger det angivna block\",\n    \"operator.integrateddynamics.block.breaksound\": \"Ljud för blockbrytning\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"Brytningsljudet för det angivna blocket\",\n    \"operator.integrateddynamics.block.placesound\": \"Ljud för blockplacering\",\n    \"operator.integrateddynamics.block.placesound.info\": \"Placeringsljudet för det angivna blocket\",\n    \"operator.integrateddynamics.block.stepsound\": \"Ljud för fotsteg på block\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"Fotstegsljudet för det angivna blocket\",\n    \"operator.integrateddynamics.block.isshearable\": \"Block kan klippas\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"Om det angivna blocket kan klippas\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"Block kan upprätthålla blockplanta\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"Om det angivna blocket kan upprätthålla den angivna blockplantan\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"Block kan upprätthålla föremålsplanta\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"Om det angivna blocket kan upprätthålla den angivna föremålsplantan\",\n    \"operator.integrateddynamics.block.plantage\": \"Plantblockets ålder\",\n    \"operator.integrateddynamics.block.plantage.info\": \"Åldern på det angivna plantblocket\",\n    \"operator.integrateddynamics.block.blockbyname\": \"Block efter namn\",\n    \"operator.integrateddynamics.itemstack\": \"Föremål\",\n    \"operator.integrateddynamics.itemstack.basename\": \"%s (föremål)\",\n    \"operator.integrateddynamics.itemstack.size\": \"Storlek\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"Den nuvarande storleken på en föremålsgrupp\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"Maxstorlek\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"Den maximala storleken på en föremålsgrupp\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"Kan grupperas\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"Om föremålsgrupper kan hålla minst två föremål.\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"Kan ta skada\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"Om föremålet kan ta skada.\",\n    \"operator.integrateddynamics.itemstack.damage\": \"Skada\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"Föremålets nuvarande skada\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"Maxskada\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"Föremålets maximala skada\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"Förtrollat\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"Om föremålet är förtrollat\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"Kan förtrollas\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"Om föremålet kan förtrollas\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"Reparationskostnad\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"Kostnaden för att reparera föremålet\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"Sällsynthet\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"Föremålets sällsynthet\",\n    \"operator.integrateddynamics.itemstack.strength\": \"Styrka gentemot block\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"Styrkan som detta föremål har gentemot det angivna blocket\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"Kan skörda\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"Om föremålet kan skörda det angivna blocket\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"Blocket från det angivna föremålet\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"Har vätska\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"Om det angivna föremålet har en vätska\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"Vätska\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"Vätskan från det angivna föremålet\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"Vätskekapacitet\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"Vätskekapaciteten för det angivna föremålet i mB\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"Likadant NBT\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"Om NBT-taggen för de angivna föremålen är densamma\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt\": \"Likadana råa föremål utan NBT\",\n    \"operator.integrateddynamics.itemstack.israwitemequal\": \"Likadana råa föremål\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"Namnet på modden som äger det angivna föremålet\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"Brinntid för bränsle\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"Bränslets brinntid i tick för det angivna föremålet\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"Är bränsle\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"Om det angivna föremålet kan användas som bränsle\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"Föremål har utrustning\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"Om det angivna föremålet har en utrustning\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"Storlek på föremålsutrustning\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"Hämtar storleken på utrustningen för den angivna föremålshanterarens innehåll\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"Föremålsutrustning\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"Hämtar utrustningen för den angivna föremålshanterarens innehåll\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"Föremål med föremålsgruppstorlek\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"Ändrar storleken på föremålsgrupper för det angivna föremålet\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"Föremål efter namn\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"Antal föremål i lista\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"Hämtar det totala antalet för det angivna föremålet i en lista.\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"Föremåls-NBT\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"Hämtar NBT-taggen för angivet föremål.\",\n    \"operator.integrateddynamics.entity\": \"Entitet\",\n    \"operator.integrateddynamics.entity.basename\": \"%s (entitet)\",\n    \"operator.integrateddynamics.entity.ismob\": \"Är ett monster\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"Om entiteten är ett monster\",\n    \"operator.integrateddynamics.entity.isanimal\": \"Är ett djur\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"Om entiteten är ett djur\",\n    \"operator.integrateddynamics.entity.isitem\": \"Är ett föremål\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"Om entiteten är ett föremål\",\n    \"operator.integrateddynamics.entity.isplayer\": \"Är en spelare\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"Om entiteten är en spelare\",\n    \"operator.integrateddynamics.entity.isminecart\": \"Är en gruvvagn\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"Om entiteten är en gruvvagn\",\n    \"operator.integrateddynamics.entity.item\": \"Föremål\",\n    \"operator.integrateddynamics.entity.item.info\": \"Föremålet från föremålsentiteten\",\n    \"operator.integrateddynamics.entity.health\": \"Hälsa\",\n    \"operator.integrateddynamics.entity.health.info\": \"Entitetens hälsa\",\n    \"operator.integrateddynamics.entity.width\": \"Bredd\",\n    \"operator.integrateddynamics.entity.width.info\": \"Entitetens bredd\",\n    \"operator.integrateddynamics.entity.height\": \"Höjd\",\n    \"operator.integrateddynamics.entity.height.info\": \"Entitetens höjd\",\n    \"operator.integrateddynamics.entity.isburning\": \"Brinner\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"Om entiteten brinner\",\n    \"operator.integrateddynamics.entity.iswet\": \"Är blöt\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"Om entiteten är blöt\",\n    \"operator.integrateddynamics.entity.iseating\": \"Äter\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"Om entiteten äter\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"Rustningsutrustning\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"Lista över föremål entiteten bär som rustning.\",\n    \"operator.integrateddynamics.entity.inventory\": \"Spelarutrustning\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"Lista över föremål spelaren bär.\",\n    \"operator.integrateddynamics.entity.mod.info\": \"Namnet på modden som äger angiven entitet\",\n    \"operator.integrateddynamics.entity.targetblock\": \"Målblock\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"Blocket den angivna entiteten för tillfället tittar på.\",\n    \"operator.integrateddynamics.entity.targetentity\": \"Målentitet\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"Entiteten som den angivna entiteten för tillfället tittar på.\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"Har gränssnitt öppet\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"Om den angivna spelaren har ett externt gränssnitt öppet.\",\n    \"operator.integrateddynamics.entity.helditem\": \"Föremål i primär hand\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"Föremålet den angivna entiteten håller i sin primära hand.\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"Föremål i sekundär hand\",\n    \"operator.integrateddynamics.entity.mounted\": \"Ridande entiteter\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"Entiteter som för tillfället rider på den angivna entiteten.\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"Föremålsramens innehåll\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"Innehållet från den angivna föremålsramen.\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"Föremålsramens rotation\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"Rotationen för angiven föremålsram.\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"Skadeljud för entitet\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"Ljudet som spelas upp när den angivna entiteten skadas.\",\n    \"operator.integrateddynamics.entity.deathsound\": \"Dödsljud för entitet\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"Ljudet som spelas upp när den angivna entiteten dör.\",\n    \"operator.integrateddynamics.entity.age\": \"Entitetsålder\",\n    \"operator.integrateddynamics.entity.age.info\": \"Åldern på en angiven entitet.\",\n    \"operator.integrateddynamics.entity.ischild\": \"Entitet är barn\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"Om den angivna entiteten är ett barn.\",\n    \"operator.integrateddynamics.entity.canbreed\": \"Entitet kan para sig\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"Om den angivna entiteten är redo att para sig.\",\n    \"operator.integrateddynamics.entity.isinlove\": \"Entitet är kär\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"Om den angivna entiteten är kär och är redo att para sig.\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"Entitet kan para sig med\",\n    \"operator.integrateddynamics.entity.isshearable\": \"Entitet kan klippas\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"Om den angivna entiteten kan klippas\",\n    \"operator.integrateddynamics.entity.nbt\": \"Entitets-NBT\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"Hämtar den angivna entiteten som NBT.\",\n    \"operator.integrateddynamics.entity.entitytype\": \"Entitetstyp\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"Namnet på entitetens typ.\",\n    \"operator.integrateddynamics.entity.entityitems\": \"Entitetsföremål\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"Föremålen som finns i denna entitet.\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"Entitetsvätskor\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"Vätskor som finns i denna entitet.\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"Lagrad entitetsenergi\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"Energimängden som lagras i denna entitet.\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"Entitetens energikapacitet\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"Energikapacitet för denna entitet.\",\n    \"operator.integrateddynamics.fluidstack\": \"Vätska\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"%s (vätska)\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"Mängd\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"Mängden mB\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"Vätskans block\",\n    \"operator.integrateddynamics.fluidstack.density\": \"Densitet\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"Vätskans densitet\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"Viskositet\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"Vätskans viskositet\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"Sällsynthet\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"Vätskans sällsynthet\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal\": \"Likadan rå vätska\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"Namnet på modden som äger den angivna vätskan\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"Vätskans NBT\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"Hämtar NBT-taggen för den angivna vätskan.\",\n    \"operator.integrateddynamics.nbt.basename\": \"%s (NBT)\",\n    \"operator.integrateddynamics.operator.basename\": \"%s (operator)\",\n    \"operator.integrateddynamics.operator.apply\": \"Tillämpa\",\n    \"operator.integrateddynamics.operator.apply.info\": \"Tillämpar ett angivet värde för en angiven operator.\",\n    \"operator.integrateddynamics.operator.apply2\": \"Tillämpa 2\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"Tillämpar två angivna värden för en angiven operator.\",\n    \"operator.integrateddynamics.operator.apply3\": \"Tillämpa 3\",\n    \"operator.integrateddynamics.operator.map\": \"Map-lista\",\n    \"operator.integrateddynamics.operator.map.info\": \"Tillämpar den angivna operatorn på alla element i en lista, resulterar i en ny lista över Map-värden.\",\n    \"operator.integrateddynamics.operator.filter.info\": \"Filtrera en lista över element genom att matcha alla med det angivna predikatet.\",\n    \"operator.integrateddynamics.operator.conjunction\": \"Förening\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"Tar föreningen av två predikat.\",\n    \"operator.integrateddynamics.operator.disjunction\": \"Skillnad\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"Tar skillnaden av två predikat.\",\n    \"operator.integrateddynamics.operator.negation.info\": \"Tar negationen av ett predikat.\",\n    \"operator.integrateddynamics.operator.pipe.info\": \"Skapar en ny operator som tar utmatningen från den första operatorn och lägger in den i den andra operatorn.\",\n    \"operator.integrateddynamics.operator.pipe2.info\": \"Skapar en ny operator som ger sin inmatning till de första och andra operatorerna, samt tar utmatningen från båda och lägger in den i den tredje operatorn.\",\n    \"operator.integrateddynamics.operator.flip\": \"Vänd\",\n    \"operator.integrateddynamics.operator.flip.info\": \"Vänder på de två första inmatningsparametrarna för en operator.\",\n    \"operator.integrateddynamics.virtual.flipped\": \"Omvänd\",\n    \"operator.integrateddynamics.operator.reduce\": \"Reducera\",\n    \"operator.integrateddynamics.operator.reduce.info\": \"Tillämpar den angivna operatorn på alla element av en lista för att reducera listan till ett värde.\",\n    \"operator.integrateddynamics.operator.reduce1\": \"Reducera 1\",\n    \"operator.integrateddynamics.operator.reduce1.info\": \"Tillämpar den angivna operatorn på alla element av en lista för att reducera listan till ett värde. reduce1(op, list) är detsamma som reduce(op, tail(list), head(list)).\",\n    \"operator.integrateddynamics.virtual.recipeoutputbyinput\": \"Receptutmatning efter inmatning\",\n    \"operator.integrateddynamics.virtual.recipeinputsbyoutput\": \"Receptinmatningar efter utmatning\",\n    \"operator.integrateddynamics.virtual.recipesbyinput\": \"Recept efter inmatning\",\n    \"operator.integrateddynamics.virtual.recipesbyoutput\": \"Recept efter utmatning\",\n    \"operator.integrateddynamics.ingredients\": \"Ingredienser\",\n    \"operator.integrateddynamics.ingredients.basename\": \"Ingredienser %s\",\n    \"operator.integrateddynamics.ingredients.items\": \"Ingrediensföremål\",\n    \"operator.integrateddynamics.ingredients.items.info\": \"Lista över föremål\",\n    \"operator.integrateddynamics.ingredients.fluids\": \"Ingrediensvätskor\",\n    \"operator.integrateddynamics.ingredients.fluids.info\": \"Lista över vätskor\",\n    \"operator.integrateddynamics.ingredients.energies\": \"Ingrediensenergielement\",\n    \"operator.integrateddynamics.ingredients.energies.info\": \"Lista över energielement\",\n    \"operator.integrateddynamics.recipe\": \"Recept\",\n    \"operator.integrateddynamics.recipe.basename\": \"%s (recept)\",\n    \"operator.integrateddynamics.recipe.input\": \"Receptets inmatningsingredienser\",\n    \"operator.integrateddynamics.recipe.input.info\": \"Inmatningsingredienserna för det angivna receptet\",\n    \"operator.integrateddynamics.recipe.output\": \"Receptets utmatningsingredienser\",\n    \"operator.integrateddynamics.recipe.output.info\": \"Utmatningsingredienserna för det angivna receptet\",\n    \"operator.integrateddynamics.general\": \"Allmänt\",\n    \"operator.integrateddynamics.general.basename\": \"%s (allmänt)\",\n    \"operator.integrateddynamics.general.choice\": \"Val\",\n    \"operator.integrateddynamics.general.choice.info\": \"Om det första värdet är sant kommer det andra värdet användas, annars används det tredje värdet.\\\\nTyp två och tre måste vara densamma.\",\n    \"operator.integrateddynamics.general.identity\": \"Identitet\",\n    \"operator.integrateddynamics.general.identity.info\": \"En kopia av inmatningsvärdet.\",\n    \"operator.integrateddynamics.general.constant\": \"Konstant\",\n    \"operator.integrateddynamics.general.constant.info\": \"En kopia av det första inmatningsvärdet.\",\n    \"operator.integrateddynamics.general.isnull\": \"Är null\",\n    \"operator.integrateddynamics.general.isnull.info\": \"Om det angivna värdet är null\",\n    \"operator.integrateddynamics.general.isnotnull\": \"Är inte null\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"Om det angivna värdet inte är null\",\n    \"operator.integrateddynamics.virtual\": \"Virtuell operator\",\n    \"operator.integrateddynamics.virtual.basename\": \"%s (virtuell)\",\n    \"operator.integrateddynamics.virtual.p_conjunction\": \"Förening\",\n    \"operator.integrateddynamics.virtual.p_conjunction.info\": \"Föreningen av två predikat\",\n    \"operator.integrateddynamics.virtual.p_disjunction\": \"Skillnad\",\n    \"operator.integrateddynamics.virtual.p_disjunction.info\": \"Skillnaden av två predikat\",\n    \"operator.integrateddynamics.virtual.p_negation\": \"Negerad\",\n    \"operator.integrateddynamics.virtual.p_negation.info\": \"Åtskillnaden av ett predikat\",\n    \"operator.integrateddynamics.virtual.piped\": \"Piped\",\n    \"operator.integrateddynamics.virtual.piped.info\": \"En kedja av operatorer\",\n    \"operator.integrateddynamics.virtual.piped2\": \"Piped 2\",\n    \"operator.integrateddynamics.cast\": \"Omvandla\",\n    \"operator.integrateddynamics.cast.basename\": \"Omvandla\",\n    \"operator.integrateddynamics.cast.tooltip\": \"Omvandla %s till %s\",\n    \"info_book.integrateddynamics.section.main\": \"Om integrerade dynamiker\",\n    \"info_book.integrateddynamics.introduction\": \"Introduktion\",\n    \"info_book.integrateddynamics.introduction.text1\": \"Möjligheterna till att automatisera har varit för begränsade i alltför lång tid. &1Integrated Dynamics&0 siktar mot att bli den bästa modden för att automatisera dina logiska system. Detta gör den genom att tillhandahålla en uttrycksfull och uppslukande logisk programmeringsupplevelse med anpassning och stöd för de flesta elementen i &lMinecraft&r och dess modifikationer.\",\n    \"info_book.integrateddynamics.introduction.text2\": \"Redstonekretsar har alltid varit en mycket enkel (men fungerande) och lättförståelig automatiseringsmetod. Enkelheten hos redstone är dock även till besvär. För mer komplicerade automatiserade nätverk kan redstonekretsar bli riktigt stora och blir därför också svåra att hantera. Dessutom kan en liten vattenläcka orsaka enorm huvudvärk för alla redstonefantaster.\",\n    \"info_book.integrateddynamics.introduction.text4\": \"Du kan navigera i denna bok genom att klicka på pilarna längst ned. Hoppa mellan avsnitt genom att Shift-klicka på pilarna istället för att bläddra en sida åt gången. I början på alla större avsnitt finns en innehållsförteckning med länkar till underavsnitten för att navigera snabbare. I recepten kan du klicka på föremålen som förklaras i boken för att snabbt komma till motsvarande avsnitt. Det finns även en knapp längst upp till vänster som tar dig till det överordnade avsnittet. Knappen längst upp till höger tar dig tillbaka till den senast besökta sidan varje gång du klickar på den.\",\n    \"info_book.integrateddynamics.tutorials\": \"Handledningar\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"Introduktion\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"Detta avsnitt innehåller flera handledningar som hjälper dig förstå grunderna för denna mod. Dessutom kommer det låta dig tjäna några &lbelöningar&r medan du genomför prestationerna i varje handledning.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"Menrilgrunder\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"För att komma igång med denna mod behöver du hitta några &lmenrilträd&r som du lättast kan hitta i &lmeneglinbiomer&r.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"Om du vill upprepa processen kan du återställa &lpressaren&r med en redstonepuls.\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"I framtiden kommer du också kunna tillverka uppgraderade varianter av dessa maskiner som är lättare att automatisera, men är samtidigt kostsammare: &lMekaniska pressaren&r och &lmekaniska torkningskaret&r.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"Grunderna i nätverk\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"Huvudmålet med denna mod är att bygga kontrollnätverk för automatisering. Detta görs med &llogiska kablar&r. Försök att göra ett nätverk med tio kablar.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"Nästa steg är att tillverka några &lvariabelkort&r och &lvariabelomvandlare&r. &lVariabelkort&r används för att lagra referenser till värden i nätverket. &lVariabelomvandlare&r är tillverkningselement för komponenter som kan placeras på &llogiska kablar&r för att läsa av och skriva dessa värden.\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"Grundläggande nätverkskomponenter\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"En metod för att binda &lvariabelkort&r till vissa värden är att läsa värden från en &lläsare&r. Vi börjar med att tillverka några enkla läsare. Tillverka sedan en &lskärmpanel&r som används för att visa värden från &lvariabelkort&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"Aspekter\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"Som ett enkelt exempel ska vi försöka läsa av ett redstonevärde och visa upp det.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"Högerklicka sedan på &lredstoneläsaren&r för att öppna gränssnittet som du sedan måste infoga ett &lvariabelkort&r i aspekten &lredstonenivå&r av typen &6heltal&0 för att binda målets redstonevärde till &lvariabelkortet&r.\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"Tillverka nu en &lredstoneskrivare&r och låt den skriva ett maximalt redstonevärde av typen heltal.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"Logiska operationer\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"Även fast att visa värden från läsare kan vara användbart kommer saker och ting bli riktigt intressanta när du börjar kombinera vissa värden med andra värden med hjälp av &2operatorer&0. Alla &2operatorer&0 finns i &llogikprogrammeraren&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"Börja med att tillverka en &llogikprogrammerare&r, som finns både som föremål och block. Öppna dess gränssnitt för att se listan över alla &loperatorer&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"Sök sedan efter operatorn &6heltal&0 och klicka på den. Detta öppnar ett fönster där du ska ange en siffa. När du har gjort det infogar du ett tomt &lvariabelkort&r för att binda detta &6heltal&0 till &lvariabelkortet&r. Efter det ska du göra det igen för ett till &6heltal&0.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"Sök till slut efter operatorn &laddition&r och öppna den. Detta låter dig lägga till två &esiffror&0. Infoga dina två &lvariabelkort&r av typen &6heltal&0 i dessa platser och bind dem till ett nytt &lvariabelkort&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling\": \"Recepthantering\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text1\": \"&oRecept&r är en stor del av &lMinecraft&r. Därför erbjuder denna mod förstklassigt stöd för dem med &8recept&0- och &lingrediens&rvärden för att representera dess in- och utmatningssamlingar. I denna handledning kommer vi titta på hur man kan få och använda recept.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text2\": \"Det enklaste sättet att få ett &8recept&0 är helt enkelt att läsa det från en maskin som kan hantera recept. Vi börjar med att läsa alla tillverkningsrecept genom att placera en &lmaskinläsare&r mot en &larbetsbänk&r och läsa listan över recept.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text3\": \"Om du vill ha en mer finjusterad kontroll över recepten eller om du arbetar med en maskin som inte exponerar sina recept via &lmaskinläsaren&r kan du själv visuellt skapa ett &8recept&0 i &llogikprogrammeraren&r. Nu ska vi försöka skapa ett enkelt recept, t.ex. för att tillverka en &lkista&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text4\": \"I den sista delen av denna handledning kommer vi ta steget vidare i receptläsningen. Med hjälp av aspekten &lrecept efter utmatning&r kan vi hämta en operator som, när den tillämpas på en &8ingrediens&0instans, kommer returnera det första &8receptet&0 som returnerar de angivna &8ingredienserna&0 som utmatning. Vi kommer använda detta för att kolla upp receptet för en &lkista&r.\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text5\": \"Vi börjar med att skapa en &8ingrediens&0instans med en &8kista&0 i din &llogikprogrammerare&r. Läs sedan aspekten &lrecept efter utmatning&r från en &lmaskinläsare&r som är riktad mot en &larbetsbänk&r. Använd därefter operatorn &otillämpa&r för att tillämpa &8ingrediensen&0 &lkista&r till operatorn som du fick från &lmaskinläsaren&r. Infoga till slut den resulterande variabeln i en &lskärmpanel&r för att se &lkistans&r recept. (Glöm inte att infoga alla dina variabler som du använde i en &lvariabellagring&r).\",\n    \"info_book.integrateddynamics.tutorials.recipe_handling.text6\": \"Självklart finns det så mycket mer som du kan göra med &8recept&0 och &8ingredienser&0. Ta en titt i &lmaskinläsaren&r och &llogikprogrammeraren&r för att ta reda på vilka andra avancerade funktioner som finns tillgängliga för dig.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"Avancerade nätverkskomponenter\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"Nu när du har blivit bekant med den grundläggande logiken kan vi ta en titt på de mer avancerande nätverkskomponenterna: &lMaterialiseraren&r och &lproxyn&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"Som du kanske kommer ihåg kan &llogikprogrammeraren&r skapa variabler med &ostatiska&r värden eller skapa mer komplicerade variabler baserade på &odynamiska&r värdeberäkningar.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"&lMaterialiseraren&r kan användas för att omvandla ett &odynamiskt&r värde till ett &ostatiskt&r. Gör detta genom att infoga det &odynamiska&r &lvariabelkortet&r i den översta platsen i &lmaterialiseraren&r. Varje &lvariabelkort&r som infogas i vänsterplatsen kommer få värdet som variabeln längst upp har för tillfället.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"&lProxyn&r låter dig lägga till ett extra abstraktionslager för dina variabler. T.ex. om du vill ha ett logiskt program som behöver ett &ostatiskt&r värde. Om du vill ändra detta värde i framtiden blir du tvungen att skriva om ditt program, såvida detta värde har behandlats med en &oproxy&r, vilket du i så fall bara behöver uppdatera värdet i &lproxyn&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"För denna handledning ska du skapa det materialiserade proxy&lvariabelkortet&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"Avancerade operationer\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"Som du kanske har märkt under den senaste handledningen kan &llogikprogrammeraren&r göra mer än att bara lägga ihop siffror. I denna handledning ska vi titta på de mer avancerade datastrukturerna och hur man länkar operatorer.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"Vi börjar med att skapa en statisk &7lista&0 över &6heltalen&0 &61&0, &610&0 och &6100&0 samt visar det sedan på en &lskärmpanel&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7Föremål&0 är en av de möjliga värdetyperna i denna mod. Vi skapar ett statiskt &lvariabelkort&r av typen &7föremål&0, hämtar dynamiskt namnet på modden från detta föremål och visar det på en &lskärmpanel&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"Kom ihåg att du måste använda en &lvariabellagring&r för att lagra din referensvariabel.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"Operatorvärden\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"Börja med att gå till &llogikprogrammeraren&r och skapa en ny statisk &2operator&0 genom att börja skriva &2operatorn&0 &oaddition (aritmetisk)&r i rullgardinslisten och klicka på den, och skriva den till ett &lvariabelkort&r. Skapa sedan två variabler av typen &6heltal&0. Hitta sedan operatorn &otillämpa&r och tillämpa det första &6heltalet&0 i &2operatorn&0, som kommer att &obinda&r det första inmatningsargumentet för additions&2operatorn&0 till detta &6heltal&0 och kommer mata ut en ny &2operator&0 med ett återstående inmatningsargument. Använd sedan operatorn &otillämpa&r igen för att &obinda&r det sista inmatningsargumentet för den nya &2operator&0 som skapades. Använd till sist en &lskärmpanel&r för att visa din dynamiskt anropade addition.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"Sedan för att tillämpa &2operatorer&0 finns det många andra avancerade &2operators&0operationer. Vi kommer nu ta en titt på hur man använder &2operatorer&0 på &7listor&0, eller mer specifikt &ofiltret&r &2operator&0 som tar en &7lista&0 och ett &opredikat&r (en &2operator&0 med ett inmatningsargument och ett &9booleskt&0 utmatningsvärde). Börja med att skapa en &7lista&0 med &6heltalen&0 &61&0, &610&0 och &6100&0. Lagra sedan &2operatorn&0 &olika med (relationell)&r i en variabel. Tillämpa denna operator på &6heltalet&0 &610&0. Använd &2operatorn&0 &ofilter&r med din &7lista&0 och den tillämpade &2operatorn&0. Detta kommer att filtrera bort alla föremål från &7listan&0 som returnerar &9falskt&0 genom att tillämpa dem på den angivna operatorn, som i detta fall innebär att alla element som inte är likadana som &610&0 kommer att tas från listan. Visa resultatet på en &lskärmpanel&r.\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"Det finns många andra operationer på &2operatorer&0, som att kombinera &opredikat&r, vända &2operators&0argument och fler operationer på &7listor&0.\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"Utmaningar\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"Det slutgiltiga handledningsavsnittet innehåller flera icke-triviala utmaningar för att visa att du förstår grunderna i &1Integrated Dynamics&0. Ingen mer information kommer att ges för varje utmaning, så lycka till!\",\n    \"info_book.integrateddynamics.manual.introduction\": \"Introduktion\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"Förkunskapskrav\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"Menrilträd\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"Emellanåt kan du stöta på långa blåa träd. Dessa träd kallas &lmenrilträd&r och har några riktigt intressanta egenskaper. För det första avger löven ljus. Detta orsakas av de självlysande bären, som också är riktigt goda! För det andra innehåller den snygga trädstammen mycket kåda som kan användas för att tillverka de flesta sakerna i denna mod.\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"Meneglinbiom\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"&lMenrilträd&r är tyvärr ganska svåra att hitta. Som tur är finns det en biom, &lmeneglinbiomen&r, där dessa träd frodas. Den stora mängden &lmenrilträd&r får många saker i denna biom att färgas blått, vilket orsakas av &lmenrilkådan&r som träden i denna miljö avger.\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"Pressare\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"Torkningskar\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"Du kan torka block och föremål med <orkningskaret&r. Detta kan t.ex. vara användbart när du vill låta din &lmenrilkåda&r torka till &lkristalliserad menril&r.\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"Vad du än gör, lägg inte in heta vätskor!\",\n    \"info_book.integrateddynamics.manual.networks\": \"Nätverk\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"&lNätverk&r i &lIntegrated Dynamics&r är grunden till alla bra automatiseringssystem. Ett nätverk består av en samling anslutna &llogiska kablar&r och delar kan placeras på dem. Dessa kablar bör hanteras med en &lblocknyckel&r. Shift+högerklicka på en kabel eller del för att ta bort den och bara högerklicka för att koppla loss den från andra kablar.\",\n    \"info_book.integrateddynamics.manual.logic\": \"Logik\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"Värdetyper\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean\": \"Boolesk\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"Den enklaste värdetypen, dess värden kan antingen vara &9sant&0 eller &9falskt&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer\": \"Heltal\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"En värdetyp som representerar positiva och negativa siffror. T.ex. &6-20&0, &60&0, &61000&0, &6333&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"Värdena kan sträcka sig mellan &6–2147483648&0 och &62147483647&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"Detta värde är en del av kategorin &lsiffror&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"Reella tal, som också vara positiva eller negativa. T.ex. &e3.1415&0, &e4815162342&0, &e3.33333333&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"Denna värdetyp är en del av kategorin &lsiffror&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"Samma som &6heltal&0, men med ett större värdesintervall, som går från &e-9223372036854775808&0 till &e9223372036854775807&0\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"Denna värdetyp är del av kategorin &lsiffror&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string\": \"Sträng\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"En teckensekvens, t.ex. &4”aaa”&0, &4“Hej!”&0, &4“x!(àà)ç”&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.list\": \"Lista\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"Ett värde som innehåller en referens till en &2operator&0. Operatorer kan delvis tillämpas för flexiblare beräkningar baserat på konceptet \\\"&lCurrying&r\\\". Detta är en del av konceptet &lmetaprogrammering&r (Det finns. Kolla upp det!), där operatorer behandlas som data från andra operatorer. Detta låter dig helt enkelt själv skapa nya operatorer, som sedan kan användas för mer komplicerade operationer.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"Du kan t.ex. lagra additions&2operatorn&0 i ett variabelkort. Gör detta genom att börja skriva operatorns namn och markera den från rullgardinslistan och klicka på den. Du kan sedan delvis tillämpa den med ett &lvariabelkort&r av typen &6heltal&0, som kommer att resultera i en ny virtuell operator med en inmatningstyp och utmatningstyp, som internt håller en referens till det tillämpade &6heltalet&0. Om ett till &6heltal&0 tillämpas blir det ett &6heltal&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"Andra operationer av &2operatorer&0 finns i &llogikprogrammeraren&r för mer avancerade beräkningar, som att reducera en lista till ett enda värde eller filtrera en lista baserat på ett &lpredikat&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"En del saker i &lMinecraft&r använder datastrukturen &3NBT&0 internt. En &3NBT&0-tagg är en speciell objekttyp som kan hålla i flera värden av olika typer. Ett exempel på en NBT-representationen är en &8kista&0 som innehåller alla sina föremål, förtrollningsinformation för en &8hacka&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"Variabler kan hålla i referenser till dessa &3NBT&0-taggar och motsvarande operatorer finns för att läsa eller utvidga dessa taggar.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"&3NBT&0-taggar används vanligtvis i denna mod som en reservmekanism på låg nivå ifall du inte kan läsa en del information med de dedikerade operatorerna.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"En referens till ett &lMinecraft&r-block. T.ex. &8jord&0, &8kullersten&0, &8rödfärgad lera&0, &8kista&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item\": \"Föremål\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"En referens till ett &lMinecraft&r-föremål. T.ex. &8äpple&0, &8kullersten&0, &8diamanthacka&0, &8förtrollat svärd&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity\": \"Entitet\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"En referens till en särskild &lMinecraft&r-entitet. T.ex. &8zombie&0, &8ko&0, &8spelare_abc&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid\": \"Vätska\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"En referens till en &lMinecraft&r-vätska. T.ex. &8lava@1000mB&0, &8vatten@3mB&0, &8menrilkåda@3030mB&0, osv.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients\": \"Ingredienser\",\n    \"info_book.integrateddynamics.manual.logic.value_types.ingredients.text1\": \"En samling &lföremål&r, &lvätskor&r och &lenergimängder&r (&6heltal&0). Dess huvudsakliga användningsområde är att representera in- och utmatningen för &8recept&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe\": \"Recept\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text1\": \"Ett abstrakt tillverkningsrecept som tar &8ingredienser&0 som inmatning och matar ut ett annat &8ingrediens&0värde.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text2\": \"Recept kan skapas visuellt med &llogikprogrammeraren&r eller läsas från maskiner (som har stöd för det) med hjälp av &lmaskingläsaren&r.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.recipe.text3\": \"Dessa recept kan användas i moddar för automatisk tillverkning, t.ex. &lIntegrated Crafting&r.\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"Variabler\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"Variabelkort\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&lVariabelkort&r är det primära läget för logisk transport i denna mod. En variabel innehåller en referens till ett speciellt &lvärde&r med en särskild &lvärdetyp&r.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"Varje &lvariabelkort&r har ett unikt ID, som används internt i nätverk för &llogisk beräkning&r. När ett fel uppstår i nätverket, kommer dessa ID:n visas för spelaren för att underlätta felsökning. För bättre användbarhet kan man lägga till en etikett på &lvariabelkort&r med en &letikettskrivare&r som låter spelare se etiketten istället för ID:n.\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"Variabellagring\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"Vissa dynamiska variabler beräknas baserat på andra variabler, där åtkomst till dessa referensvariabler måste vara möjligt. En dynamisk variabel som representerar additionen av andra variabler måste kunna komma åt dessa två andra variabler när den beräknas. Åtkomst till dessa andra variabler måste vara möjligt inom samma nätverk där den dynamiska variabeln beräknas, som kan göras genom att placera de andra variabelkorten i en &lvariabellagring&r. Flera &lvariabellagringar&r kan finnas i ett nätverk. Dessa andra variabler kan också hänvisa till särskilda variabler, som också bör vara finnas i samma nätverk.\",\n    \"info_book.integrateddynamics.manual.parts\": \"Delar\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"Introduktion\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text3\": \"&lEnergikanalen&r för en del bestämmer var den kan dra sin energi från. -1 är kanalen som matchar alla kanaler och 0 är standardkanalen (används av delar som energiceller som inte har en kanalinställning). Slutligen bestämmer &lmålsidan&r vilken sida av dess mål som kommer att be interageras med. Standardvärdet är sidan som den är placerad mot.\",\n    \"info_book.integrateddynamics.manual.parts.introduction.text4\": \"En del läs- och skrivdelar innehåller endast aspekter när vissa andra moddar används. Om inga aspekter finns för den särskild del kommer en varning läggas till i föremålets inforuta när du försöker tillverka den.\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"Läsare\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"Introduktion\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"Ljud\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"Information om musik och ljud som spelas i miljön.\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"Information om blocket som delen är riktad mot.\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"Entitet\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"Information om entiteten eller entiteter som är inuti fyrkanten som delen är riktad mot.\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"Extradimensionell\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"Information om servern, t.ex. listan över anslutna spelare.\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"Vätska\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"Läs värden från vätsketanken som denna del är riktad mot.\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"Utrustning\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"Läs information från den inriktade utrustningen, som antalet platser och föremål. Detta kommer inte överföra själva föremålen.\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"Maskin\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"Maskinen som denna del är riktad mot kan läsas av, som arbetet de utför.\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"Nätverk\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"Läs information om &lIntegrated Dynamics&r-nätverket som denna del är riktad mot. Detta behöver inte vara samma nätverk som nätverket denna del är på.\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"Läs redstoneinformation från blocket den är riktad mot.\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"Värld\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"Information om världen som denna del är i, som nuvarande väder och tid.\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"Skrivare\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"Ljud\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"Spelar upp ljud och musik.\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"Effekt\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"Avge partiklar baserat på särskilda värden och inställningar.\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity\": \"Entitet\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"Konfigurera och kontrollera entiteter.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine\": \"Maskin\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"Konfigurera och kontrollera maskiner.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"Utrustning\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"Utför handlingar som är relaterade till utrustningar och rör. Detta kommer inte överföra själva föremålen.\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"Matar ut en särskild redstonenivå.\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"Övrigt\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"Ljus\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"Ett enkelt ljus som antingen är alltid igång, eller kan kontrolleras med ett &lvariabelkort&r av typen &1boolesk&0 eller &6heltal&0.\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"Skärmar\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"Ett användbart verktyg för att övervaka eller felsöka som helt enkelt visar värdet för ett &lvariabelkort&r. Högerklicka med en &lblocknyckel&r på en &lskärm&r för att rotera den.\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text2\": \"Inuti gränssnittet finns knappen &lKopiera&r som kopierar värdet som visas till ditt urklipp så att det kan användas utanför spelet. Du kan också bara använda standardkortkommandot i ditt operativsystem för att kopiera för att kopiera detta värde.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"Enriktad anslutare\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"Ibland kommer ditt logiska nätverk sträcka sig över längre avstånd. Fast vanliga kablar fungerar bra kommer den inte vara speciellt lämpligt att placera kablar över längre sträckor.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&lEnriktade anslutare&r låter dig ansluta trådlösa nätverk över långa avstånd. Placera en anslutare på ett nätverk och en annan anslutare på ett annat nätverk. Dessa två anslutare måste vara i en rak linje i förhållande till varandra med ett avstånd mindre eller lika med 512 block. Anslutaren kommer bli aktiv när en giltig anslutning har gjorts.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"Flerriktad anslutare\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"Fast &lenriktade&r anslutare är användbara för anslutningar på långa avstånd i en riktning kan detta vara lite för begränsat i vissa fall.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"Du kanske t.ex. vill ansluta ihop fler än två nätverk med en anslutare eller ansluta till nätverk i andra dimensioner. I dessa fall kan du använda &lflerriktade&r anslutare.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"Dessa anslutare kommer ha ett unikt grupp-ID när den tillverkas vilket identifierar gruppen som nätverket kan anslutas till. Varje anslutargrupp har en unik partikelfärg när den placeras i världen. Grupp-ID:et syns i föremålets inforuta eller genom att högerklicka på anslutaren efter att den har placerats. Fler anslutare kan läggas till i en grupp genom att tillverka en ny med en befintlig anslutare inom en grupp.\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"Du kan också ändra gruppen med befintliga anslutare på två sätt. När en tillverkas i en arbetsbänk kommer den tilldelas till en ny grupp utan andra medlemmar och när två tillverkas tillsammans kommer gruppen för den första att kopieras till den andra.\",\n    \"info_book.integrateddynamics.manual.machines\": \"Maskiner\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"Materialiserare\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"För att göra en dynamiskt &lvariabelkort&r statiskt kan en &lmaterialiserare&r användas. Det dynamiska &lvariabelkortet&r måste infogas och ett nytt tomt &lvariabelkort&r måste infogas, som kommer få den att innehålla det dynamiska &lvariabelkortets&r nuvarande värde som ett statiskt värde.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"Detta kan vara användbart i situationer där du vill logga värden för ett specifikt dynamiskt &lvariabelkort&r, eller när du inte längre behöver den dynamiska beräkningen av ett &lvariabelkort&r.\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&lVariabelkort&r kan anslutas med rör in och ut från &lmaterialiseraren&r.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"En &lproxy&r kan användas för att skapa en dynamisk referens till ett särskilt &lvariabelkort&r. Den låter dig infoga ett &lvariabelkort&r i platsen längst upp och skriva ett nytt &lvariabelkort&r, som dynamiskt kommer ta värdet från &lvariabelkortet&r i proxyn. Observera att referens&lvariabelkortet&r (eller något annat) måste ligga i den övre platserna för att proxyn ska fungera.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"Detta är användbart i situationer där du har en kedja av &lvariabelkort&r men du vill fortfarande kunna ändra ett värde manuellt. Du kan sedan behandla variabeln i proxyn och låta den bytas ut med en annan variabel när som helst.\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"Precis som med &lmaterialiseraren&r kan &lvariabelkort&r anslutas med rör in och ut från den.\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"I vissa situationer behöver du kanske upprätthålla någon slags historik över variabelvärden. När du t.ex. vill ta medelvärdet eller skillnaden över de senaste värdena.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"Energibatteri\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"&lIntegrated Dynamics&r-nätverk kan konfigureras till att konsumera energi. För att göra detta kan du fästa &lenergibatterier&r till nätverket för att lägga till energilagringskapacitet.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"När en redstonesignal tillämpas kommer det automatiska energiexporteringsläget aktiveras på alla sidor.\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"När du har detta batteri i din utrustning kan läget &lautofyll&r aktiveras genom att Shift+högerklicka på det. När det är aktiverat kommer detta batteri automatiskt försöka fylla föremålen du håller i båda händerna med energi.\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"Mekanisk pressare\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"Mekaniskt torkningskar\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"Det kan ta en stund att torka med ett <orkningskar&r. Om du vill att saker och ting ska gå snabbare eller om du vill kunna torka fler saker kan du använda det &lmekaniska torkningskaret&r som behöver energi för att köras.\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"Logisk programmering\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"En annan metod för att skapa dynamiska variabelkort är att tillämpa en &loperator&r på ett eller flera &lvariabelkort&r för att producera ett nytt &lvariabelkort&r. Ett exempel på en &loperator&rsaddition av två &lsiffror&r, som resulterar i en ny &lsiffra&r.\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"&lLogikprogrammeraren&r innehåller en stor samling &loperatorer&r. Den låter dig skapa statiska &lvariabelkort&r för alla &lvärdetyper&r. Efter att ha valt en &loperator&r, bör ett giltigt &lvariabelkort&r infogas i den förväntade inmatningsplatserna. Slutligen bör ett till &lvariabelkort&r infogas i utmatningsplatsen. Om &loperatorn&r är giltig kommer en ny dynamisk variabel skrivas till kortet. Kom ihåg att referens&lvariabelkortet&r måste finns i en &lvariabellagring&r i samma nätverk där &loperatorn&r kommer att beräknas.\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/tr_tr.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/uk_ua.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/vi_vn.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/zh_cn.json",
    "content": "{\n    \"itemGroup.integrateddynamics\": \"动态联合\",\n    \"general.integrateddynamics.description\": \"充分自动化控制您的机器。\",\n    \"general.integrateddynamics.info.none\": \"无\",\n    \"general.integrateddynamics.energy\": \"能量\",\n    \"general.integrateddynamics.error.no_network\": \"找不到有效的网络\",\n    \"general.integrateddynamics.true\": \"True(真)\",\n    \"general.integrateddynamics.false\": \"False(假)\",\n    \"gui.integrateddynamics.part_settings\": \"部分设定\",\n    \"gui.integrateddynamics.aspect_settings\": \"检测设定\",\n    \"gui.integrateddynamics.keybinding\": \"按键绑定\",\n    \"gui.integrateddynamics.button.save\": \"保存\",\n    \"gui.integrateddynamics.button.rename\": \"重命名\",\n    \"gui.integrateddynamics.button.copy\": \"复制\",\n    \"gui.integrateddynamics.button.copy.info\": \"复制到剪切板\",\n    \"gui.integrateddynamics.button.edit\": \"编辑\",\n    \"gui.integrateddynamics.button.add\": \"增加\",\n    \"gui.integrateddynamics.button.remove\": \"删除\",\n    \"gui.integrateddynamics.partsettings.update_interval\": \"频率\",\n    \"gui.integrateddynamics.partsettings.priority\": \"优先级\",\n    \"gui.integrateddynamics.partsettings.channel\": \"能量频道\",\n    \"gui.integrateddynamics.partsettings.channel.disabledinfo\": \"此服务器上的网络能耗已被禁用\",\n    \"gui.integrateddynamics.partsettings.side\": \"目标面\",\n    \"gui.integrateddynamics.delay.capacity\": \"容量\",\n    \"gui.integrateddynamics.part.properties\": \"属性:\",\n    \"gui.integrateddynamics.logicprogrammer.filter\": \"过滤\",\n    \"gui.integrateddynamics.rewards\": \"奖励\",\n    \"gui.integrateddynamics.rewards.collect\": \"收集\",\n    \"gui.integrateddynamics.rewards.collected\": \"收集的\",\n    \"gui.integrateddynamics.rewards.collectFailure\": \"无法获得奖励, 你的物品栏满了吗?\",\n    \"gui.integrateddynamics.input\": \"输入: %s\",\n    \"gui.integrateddynamics.output\": \"输出: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.fluidautoeject\": \"自动输出流体\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject\": \"自动输出流体: %s\",\n    \"gui.integrateddynamics.mechanical_squeezer.togglefluidautoeject.info\": \"将流体自动输出到附近的容器.\",\n    \"gui.integrateddynamics.input_short\": \"输入\",\n    \"gui.integrateddynamics.output_short\": \"输出\",\n    \"gui.integrateddynamics.recipe.strictnbt\": \"严格NBT\",\n    \"gui.integrateddynamics.recipe.tagvariants\": \"物品标签变种\",\n    \"gui.integrateddynamics.diagnostics.title\": \"网络诊断\",\n    \"gui.integrateddynamics.diagnostics.parts\": \"部分设定\",\n    \"gui.integrateddynamics.diagnostics.table.network\": \"网络\",\n    \"gui.integrateddynamics.diagnostics.table.cables\": \"导线\",\n    \"gui.integrateddynamics.diagnostics.table.part\": \"部件\",\n    \"gui.integrateddynamics.diagnostics.table.ticktime\": \"游戏刻 (ms)\",\n    \"gui.integrateddynamics.diagnostics.table.dimension\": \"维度\",\n    \"gui.integrateddynamics.diagnostics.table.position\": \"位置\",\n    \"gui.integrateddynamics.diagnostics.table.side\": \"面\",\n    \"key.categories.integrateddynamics\": \"动态联合\",\n    \"key.integrateddynamics.logic_programmer_focus_search\": \"在逻辑编程器中搜索\",\n    \"key.integrateddynamics.logic_programmer_open_rename\": \"在逻辑编程器重命名变量卡\",\n    \"subtitles.integrateddynamics.effect.page.flipsingle\": \"页面：翻动\",\n    \"subtitles.integrateddynamics.effect.page.flipmultiple\": \"页面：翻动\",\n    \"block.integrateddynamics.cable\": \"逻辑线缆\",\n    \"block.integrateddynamics.variablestore\": \"变量卡箱\",\n    \"block.integrateddynamics.energy_battery\": \"能量电池\",\n    \"block.integrateddynamics.energy_battery.info\": \"与其他能量电池放在合成网格中以增加容量。\",\n    \"block.integrateddynamics.energy_battery.info.auto_supply\": \"自动输出: %s\",\n    \"block.integrateddynamics.energy_battery_creative\": \"创造能量电池\",\n    \"block.integrateddynamics.energy_battery_creative.info.auto_supply\": \"自动输出: %s\",\n    \"block.integrateddynamics.logic_programmer\": \"逻辑编程器\",\n    \"block.integrateddynamics.coal_generator\": \"发电机\",\n    \"block.integrateddynamics.coal_generator.info\": \"从燃料中获取能量\",\n    \"block.integrateddynamics.proxy\": \"代理器\",\n    \"block.integrateddynamics.proxy.info\": \"将一个变量卡输出为一个检测类型。在合成网格中清除。\",\n    \"block.integrateddynamics.materializer\": \"复制器\",\n    \"block.integrateddynamics.materializer.info\": \"复制变量的数据作为新的变量\",\n    \"block.integrateddynamics.menril_log\": \"门瑞欧原木\",\n    \"block.integrateddynamics.menril_log_filled\": \"富集门瑞欧原木\",\n    \"block.integrateddynamics.menril_leaves\": \"门瑞欧树叶\",\n    \"block.integrateddynamics.menril_sapling\": \"门瑞欧树苗\",\n    \"block.integrateddynamics.menril_planks\": \"门瑞欧木板\",\n    \"block.integrateddynamics.crystalized_menril_block\": \"门瑞欧结晶方块\",\n    \"block.integrateddynamics.crystalized_menril_brick\": \"门瑞欧结晶砖块\",\n    \"block.integrateddynamics.block_menril_resin\": \"门瑞欧树脂\",\n    \"block.integrateddynamics.drying_basin\": \"烘干池\",\n    \"block.integrateddynamics.drying_basin.info\": \"烘干物品和流体\",\n    \"block.integrateddynamics.squeezer\": \"挤压机\",\n    \"block.integrateddynamics.squeezer.info\": \"用跳跃来挤压,弄平,粉碎\",\n    \"block.integrateddynamics.menril_torch\": \"门瑞欧火把\",\n    \"block.integrateddynamics.menril_torch_stone\": \"门瑞欧石火把\",\n    \"block.integrateddynamics.invisible_light\": \"不可见光\",\n    \"block.integrateddynamics.menril_door\": \"门瑞欧木门\",\n    \"block.integrateddynamics.menril_planks_stairs\": \"门瑞欧楼梯\",\n    \"block.integrateddynamics.crystalized_menril_block_stairs\": \"门瑞欧结晶楼梯\",\n    \"block.integrateddynamics.crystalized_menril_brick_stairs\": \"门瑞欧结晶砖楼梯\",\n    \"block.integrateddynamics.delay\": \"延迟器\",\n    \"block.integrateddynamics.delay.info\": \"将变量的历史显示在一个面上。\",\n    \"block.integrateddynamics.block_liquid_chorus\": \"紫颂果浆\",\n    \"block.integrateddynamics.crystalized_chorus_block\": \"紫颂果浆结晶方块\",\n    \"block.integrateddynamics.crystalized_chorus_brick\": \"紫颂果浆结晶砖块\",\n    \"block.integrateddynamics.crystalized_chorus_block_stairs\": \"紫颂果浆结晶楼梯\",\n    \"block.integrateddynamics.crystalized_chorus_brick_stairs\": \"紫颂果浆结晶砖楼梯\",\n    \"block.integrateddynamics.mechanical_squeezer\": \"电动挤压机\",\n    \"block.integrateddynamics.mechanical_squeezer.info\": \"消耗能量(RF)来挤压物品.\",\n    \"block.integrateddynamics.mechanical_drying_basin\": \"电动烘干池\",\n    \"block.integrateddynamics.mechanical_drying_basin.info\": \"消耗能量(RF)来烘干物品和流体.\",\n    \"item.integrateddynamics.wrench\": \"扳手\",\n    \"item.integrateddynamics.variable\": \"变量卡\",\n    \"item.integrateddynamics.variable.info\": \"在合成台清除或复制\",\n    \"item.integrateddynamics.variable.warning\": \"§4§l警告:千万不要在创造模式中复制它!\",\n    \"item.integrateddynamics.variable.id\": \"§e§o变量ID: §r§o%s\",\n    \"item.integrateddynamics.labeller\": \"注释器\",\n    \"item.integrateddynamics.labeller.info\": \"命名你的变量卡。它也可以在逻辑编程器内使用,需要持有注释器\",\n    \"item.integrateddynamics.labeller.button.write\": \"写入\",\n    \"item.integrateddynamics.facade\": \"伪装块\",\n    \"item.integrateddynamics.facade.info\": \"在工作台结合一个方块\",\n    \"item.integrateddynamics.bucket_liquid_chorus\": \"紫颂果浆桶\",\n    \"item.integrateddynamics.bucket_menril_resin\": \"门瑞欧树脂桶\",\n    \"item.integrateddynamics.crystalized_menril_chunk\": \"门瑞欧结晶碎块\",\n    \"item.integrateddynamics.variable_transformer\": \"变量输入/输出\",\n    \"item.integrateddynamics.variable_transformer_output\": \"变量输出\",\n    \"item.integrateddynamics.variable_transformer_input\": \"变量输入\",\n    \"item.integrateddynamics.menril_berries\": \"门瑞欧浆果\",\n    \"item.integrateddynamics.menril_berries.info\": \"美味又明亮\",\n    \"item.integrateddynamics.portable_logic_programmer\": \"便携式逻辑编辑器\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration\": \"论动态联合\",\n    \"item.integrateddynamics.on_the_dynamics_of_integration.info\": \"一个完整的mod参考指南与教程\",\n    \"item.integrateddynamics.crystalized_chorus_chunk\": \"紫颂果浆结晶碎块\",\n    \"item.integrateddynamics.logic_director\": \"逻辑指向器\",\n    \"item.integrateddynamics.proto_chorus\": \"远古紫颂果\",\n    \"advancement.integrateddynamics.meneglin_discovery\": \"发现门勒格林\",\n    \"advancement.integrateddynamics.meneglin_discovery.desc\": \"砍下位于门勒格林的门瑞欧树\",\n    \"advancement.integrateddynamics.squeezing\": \"专业级别的挤压!\",\n    \"advancement.integrateddynamics.squeezing.desc\": \"制作一台挤压机\",\n    \"advancement.integrateddynamics.drying\": \"专业级别的烘干!\",\n    \"advancement.integrateddynamics.drying.desc\": \"制作一个洪干池\",\n    \"advancement.integrateddynamics.menril_production\": \"门瑞欧加工\",\n    \"advancement.integrateddynamics.cables_logic\": \"逻辑线缆\",\n    \"advancement.integrateddynamics.cables_logic.desc\": \"制作一个逻辑线缆\",\n    \"advancement.integrateddynamics.networks_logic\": \"逻辑网络\",\n    \"advancement.integrateddynamics.networks_logic.desc\": \"构建具有至少10个相互连接的逻辑电缆的逻辑网络。\",\n    \"advancement.integrateddynamics.menril_wrenching\": \"门瑞欧扳手\",\n    \"advancement.integrateddynamics.menril_wrenching.desc\": \"制作一个扳手\",\n    \"advancement.integrateddynamics.variables\": \"变量评测\",\n    \"advancement.integrateddynamics.variables.desc\": \"制作一个变量卡\",\n    \"advancement.integrateddynamics.variable_input\": \"变量输入转换\",\n    \"advancement.integrateddynamics.variable_output\": \"变量输出装换器\",\n    \"advancement.integrateddynamics.redstone_reading\": \"红石读取\",\n    \"advancement.integrateddynamics.redstone_reading.desc\": \"制作一个红石读取器\",\n    \"advancement.integrateddynamics.block_reading\": \"方块读取\",\n    \"advancement.integrateddynamics.block_reading.desc\": \"制作一个方块读取器\",\n    \"advancement.integrateddynamics.inventory_reading\": \"物品栏读取\",\n    \"advancement.integrateddynamics.inventory_reading.desc\": \"制作一个物品栏读取器\",\n    \"advancement.integrateddynamics.machine_reading\": \"机器读取\",\n    \"advancement.integrateddynamics.machine_reading.desc\": \"制作一个机器读取器\",\n    \"advancement.integrateddynamics.value_displaying\": \"数据显示\",\n    \"advancement.integrateddynamics.value_displaying.desc\": \"制作一个显示面板\",\n    \"advancement.integrateddynamics.redstone_capturing\": \"红石信号捕捉\",\n    \"advancement.integrateddynamics.redstone_capturing.desc\": \"制作一个红石§6Integer(整型)§7数据的变量卡。\",\n    \"advancement.integrateddynamics.redstone_observement\": \"红石观测\",\n    \"advancement.integrateddynamics.redstone_observement.desc\": \"放置一个§6Integer(整型)§7变量卡到显示面板里。\",\n    \"advancement.integrateddynamics.redstone_transmission\": \"红石信号传输\",\n    \"advancement.integrateddynamics.redstone_transmission.desc\": \"让一个红石输出器输出一个最大等级的红石信号。\",\n    \"advancement.integrateddynamics.logic_programming\": \"逻辑编程\",\n    \"advancement.integrateddynamics.logic_programming.desc\": \"打开逻辑编辑器的界面\",\n    \"advancement.integrateddynamics.constant_definition\": \"常量定义\",\n    \"advancement.integrateddynamics.constant_definition.desc\": \"定义一个常量§6Integer(整型)§7变量卡。\",\n    \"advancement.integrateddynamics.arithmetic_addition\": \"算数加法\",\n    \"advancement.integrateddynamics.arithmetic_addition.desc\": \"相加§e数字§7并显示其结果。\",\n    \"advancement.integrateddynamics.variable_materialization\": \"变量复制\",\n    \"advancement.integrateddynamics.variable_materialization.desc\": \"在§l复制器§r中复制一个变量。\",\n    \"advancement.integrateddynamics.variable_proxying\": \"变量代理\",\n    \"advancement.integrateddynamics.variable_proxying.desc\": \"在§l代理器§r中创建一个变量代理。\",\n    \"advancement.integrateddynamics.logical_list_building\": \"逻辑列表构建。\",\n    \"advancement.integrateddynamics.logical_list_building.desc\": \"构建一个静态列表§6Integers(整型)§7 1, 10 和 100。\",\n    \"advancement.integrateddynamics.item_origin_identification\": \"物品初始ID\",\n    \"advancement.integrateddynamics.item_origin_identification.desc\": \"获得一个物品的mod名称。\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at\": \"我在看什么？\",\n    \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\": \"获取你视线所指向的方块。\",\n    \"advancement.integrateddynamics.dynamic_additions\": \"动态添加\",\n    \"advancement.integrateddynamics.dynamic_additions.desc\": \"动态调用加法运算符\",\n    \"advancement.integrateddynamics.dynamic_list_filtering\": \"动态列表过滤\",\n    \"advancement.integrateddynamics.dynamic_list_filtering.desc\": \"过滤一个整型列表 1, 10 和 100 到 10.\",\n    \"advancement.integrateddynamics.creeper_taming\": \"驯服爬行者\",\n    \"advancement.integrateddynamics.creeper_taming.desc\": \"在显示面板显示一只爬行者的血量\",\n    \"advancement.integrateddynamics.sponge_step_sound\": \"踩海绵音效\",\n    \"advancement.integrateddynamics.sponge_step_sound.desc\": \"播放踩在湿海绵上的音效\",\n    \"advancement.integrateddynamics.recursive_recursion\": \"递归\",\n    \"advancement.integrateddynamics.recursive_recursion.desc\": \"创建一个无限列表并将它显示在显示面板上\",\n    \"advancement.integrateddynamics.tool_for_obsidian\": \"能否采掘黑曜石?\",\n    \"advancement.integrateddynamics.tool_for_obsidian.desc\": \"测试一把石镐是否能够破坏黑曜石,并将结果显示在显示面板上。\",\n    \"advancement.integrateddynamics.smart_pressure_plate\": \"智能压力板\",\n    \"advancement.integrateddynamics.smart_pressure_plate.desc\": \"制作一个自动开门系统 。经过一个基于实体名称匹配的实体读取器时, 红石输出器向门输出信号。\",\n    \"valuetype.integrateddynamics.error.value\": \"无效数据\",\n    \"valuetype.integrateddynamics.value_type\": \"数据类型\",\n    \"valuetype.integrateddynamics.tooltip.type_name\": \"§e类型:§r%s\",\n    \"valuetype.integrateddynamics.tooltip.value\": \"§e§o值:§r%s\",\n    \"valuetype.integrateddynamics.operator.tooltip.signature\": \"§e签名:§r%s\",\n    \"valuetype.integrateddynamics.error.invalid_input\": \"输入的\\\"%s\\\"不能被解析为这个类型\",\n    \"valuetype.integrateddynamics.error.invalid_input_item\": \"这个类型需要一个输入物品\",\n    \"valuetype.integrateddynamics.error.invalid_list_element\": \"元素%s不合法: %s\",\n    \"valuetype.integrateddynamics.error.invalid_list_value_type\": \"此列表请求数据类型%s与当前指定的%s不匹配\",\n    \"valuetype.integrateddynamics.error.invalid_operator_operator\": \"位置%s所期望的的运算符与当前指定的%s不匹配\",\n    \"valuetype.integrateddynamics.error.invalid_operator_signature\": \"运算符%s所期望的数据类型 (%s) 与当前指定数据的类型(%s) 不匹配\",\n    \"valuetype.integrateddynamics.error.block.no_block\": \"插入的物品不是方块\",\n    \"valuetype.integrateddynamics.error.fluid.no_fluid\": \"插入的物品不包含流体\",\n    \"valuetype.integrateddynamics.any\": \"Any(任意)\",\n    \"valuetype.integrateddynamics.any.info\": \"接受任何类型的数据\",\n    \"valuetype.integrateddynamics.number\": \"Number(数值)\",\n    \"valuetype.integrateddynamics.number.info\": \"接受所有类型的数字\",\n    \"valuetype.integrateddynamics.named\": \"转为字符串\",\n    \"valuetype.integrateddynamics.named.info\": \"将值转换为字符串的形式 (将数值1.2转换为字符串\\\"1.2\\\")\",\n    \"valuetype.integrateddynamics.uniquely_named\": \"转为唯一名称\",\n    \"valuetype.integrateddynamics.uniquely_named.info\": \"将值转换为它的唯一名称\",\n    \"valuetype.integrateddynamics.boolean\": \"Boolean(布尔)\",\n    \"valuetype.integrateddynamics.integer\": \"Integer(整型)\",\n    \"valuetype.integrateddynamics.double\": \"Double(双精度型)\",\n    \"valuetype.integrateddynamics.long\": \"Long(长整型)\",\n    \"valuetype.integrateddynamics.string\": \"String(字符串)\",\n    \"valuetype.integrateddynamics.operator\": \"Operator(运算符)\",\n    \"valuetype.integrateddynamics.list\": \"List(列表)\",\n    \"valuetype.integrateddynamics.block\": \"Block(方块)\",\n    \"valuetype.integrateddynamics.itemstack\": \"Item(物品)\",\n    \"valuetype.integrateddynamics.entity\": \"Entity(实体)\",\n    \"valuetype.integrateddynamics.fluidstack\": \"Fluid(流体)\",\n    \"parttype.integrateddynamics.error.low_energy\": \"能源不足\",\n    \"parttype.integrateddynamics.tooltip.disabled\": \"关闭\",\n    \"parttype.integrateddynamics.tooltip.inactive\": \"激活\",\n    \"parttype.integrateddynamics.tooltip.errors\": \"错误:\",\n    \"parttype.integrateddynamics.tooltip.writer.active_aspect\": \"设置面: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.display.active_value\": \"值: %s (%s)\",\n    \"parttype.integrateddynamics.tooltip.monodirectionalconnector.group\": \"分组: %s\",\n    \"parttype.integrateddynamics.tooltip.noaspects\": \"警告: 没有可用的扩展. 当安装其他mod后可能生效.\",\n    \"parttype.integrateddynamics.audio_reader\": \"声音读取器\",\n    \"parttype.integrateddynamics.audio_reader.info\": \"读取声音信息\",\n    \"parttype.integrateddynamics.block_reader\": \"方块读取器\",\n    \"parttype.integrateddynamics.block_reader.info\": \"读取方块信息\",\n    \"parttype.integrateddynamics.entity_reader\": \"实体读取器\",\n    \"parttype.integrateddynamics.entity_reader.info\": \"读取实体信息\",\n    \"parttype.integrateddynamics.extradimensional_reader\": \"附加维度读取器\",\n    \"parttype.integrateddynamics.extradimensional_reader.info\": \"读取所有维度共享的信息\",\n    \"parttype.integrateddynamics.fluid_reader\": \"流体读取器\",\n    \"parttype.integrateddynamics.fluid_reader.info\": \"读取流体容器信息\",\n    \"parttype.integrateddynamics.inventory_reader\": \"物品容器读取器\",\n    \"parttype.integrateddynamics.inventory_reader.info\": \"读取物品容器信息\",\n    \"parttype.integrateddynamics.network_reader\": \"网络读取器\",\n    \"parttype.integrateddynamics.network_reader.info\": \"读取目标网络预设\",\n    \"parttype.integrateddynamics.machine_reader\": \"机器读取器\",\n    \"parttype.integrateddynamics.machine_reader.info\": \"读取机器信息\",\n    \"parttype.integrateddynamics.redstone_reader\": \"红石读取器\",\n    \"parttype.integrateddynamics.redstone_reader.info\": \"读取红石信号\",\n    \"parttype.integrateddynamics.world_reader\": \"世界读取器\",\n    \"parttype.integrateddynamics.world_reader.info\": \"读取世界,方块,实体的相关信息\",\n    \"parttype.integrateddynamics.audio_writer\": \"声音输出\",\n    \"parttype.integrateddynamics.audio_writer.info\": \"输出声音和音乐\",\n    \"parttype.integrateddynamics.effect_writer\": \"效果输出\",\n    \"parttype.integrateddynamics.effect_writer.info\": \"输出粒子效果\",\n    \"parttype.integrateddynamics.entity_writer\": \"实体写入器\",\n    \"parttype.integrateddynamics.entity_writer.info\": \"为实体写入配置\",\n    \"parttype.integrateddynamics.machine_writer\": \"机器写入器\",\n    \"parttype.integrateddynamics.machine_writer.info\": \"为机器写入配置\",\n    \"parttype.integrateddynamics.inventory_writer\": \"物品容器输出\",\n    \"parttype.integrateddynamics.inventory_writer.info\": \"写入物品容器\",\n    \"parttype.integrateddynamics.redstone_writer\": \"红石输出\",\n    \"parttype.integrateddynamics.redstone_writer.info\": \"输出红石信号\",\n    \"parttype.integrateddynamics.data_driven_panel.error.invalid_type\": \"这个面板不接受一个%s.\",\n    \"parttype.integrateddynamics.static_light_panel\": \"静态光源\",\n    \"parttype.integrateddynamics.dynamic_light_panel\": \"动态光源\",\n    \"parttype.integrateddynamics.display_panel\": \"显示器\",\n    \"parttype.integrateddynamics.display_panel.info\": \"用文本显示变量的值\",\n    \"parttype.integrateddynamics.connector_mono_directional\": \"单向连接器\",\n    \"parttype.integrateddynamics.connector_mono_directional.info\": \"可以在一条直线上与一定范围内与其他单向连接器连接\",\n    \"parttype.integrateddynamics.connector_omni_directional\": \"全向连接器\",\n    \"parttype.integrateddynamics.connector_omni_directional.info\": \"可以与任意位置的全向连接器连接,包括不同的维度\",\n    \"variable.integrateddynamics.error.invalid_item\": \"当前变量不能被用在这个检测类型\",\n    \"variable.integrateddynamics.error.part_not_in_network\": \"在当前网络中未找到部件 ID:%s\",\n    \"variable.integrateddynamics.error.recursion\": \"ID为%s的变量引用了自己\",\n    \"proxy.integrateddynamics.tooltip.proxy_id\": \"§e§o代理ID: §r§o%s\",\n    \"proxy.integrateddynamics.error.proxy_not_in_network\": \"当前网络中找不到ID为%s的代理器\",\n    \"proxy.integrateddynamics.error.proxy_invalid\": \"ID为%s的代理器内没有变量卡\",\n    \"proxy.integrateddynamics.error.proxy_invalid_type\": \"ID为%s的代理器期望绑定类型为%s的变量而不是%s\",\n    \"delay.integrateddynamics.tooltip.delay_id\": \"§e§o延迟 ID: §r§o%s\",\n    \"delay.integrateddynamics.error.delay_not_in_network\": \"当前网络中找不到ID为%s的延迟器\",\n    \"delay.integrateddynamics.error.delay_invalid\": \"ID为%s的延迟器内没有变量卡\",\n    \"delay.integrateddynamics.error.delay_invalid_type\": \"ID为%s的延迟器期望绑定类型为%s的变量而不是%s\",\n    \"aspect.integrateddynamics.tooltip.aspect_name\": \"§e检测类型: §r%s\",\n    \"aspect.integrateddynamics.tooltip.value_type_name\": \"§e数据类型: §r%s\",\n    \"aspect.integrateddynamics.tooltip.part_id\": \"§e§o部件ID: §r§o%s\",\n    \"aspect.integrateddynamics.error.invalid_type\": \"这个检测类型期望%s而不是%s\",\n    \"aspect.integrateddynamics.name\": \"检测类型\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare\": \"音符盒-小鼓\",\n    \"aspect.integrateddynamics.read.integer.audio.instrument.snare.info\": \"读取小鼓的声音,接受范围[0, 24]\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare\": \"音符盒-小鼓\",\n    \"aspect.integrateddynamics.write.integer.audio.instrument.snare.info\": \"发出小鼓声音,接受范围[0, 24]\",\n    \"aspect.integrateddynamics.write.string.audio.sound\": \"音乐\",\n    \"aspect.integrateddynamics.write.string.audio.sound.info\": \"播放指定的音乐\",\n    \"aspect.integrateddynamics.read.boolean.block.block\": \"有方块\",\n    \"aspect.integrateddynamics.read.boolean.block.block.info\": \"目标位置是否有方块\",\n    \"aspect.integrateddynamics.read.integer.block.posx\": \"X坐标\",\n    \"aspect.integrateddynamics.read.integer.block.posx.info\": \"目标的X坐标\",\n    \"aspect.integrateddynamics.read.integer.block.posy\": \"Y坐标\",\n    \"aspect.integrateddynamics.read.integer.block.posy.info\": \"目标的Y坐标\",\n    \"aspect.integrateddynamics.read.integer.block.posz\": \"Z坐标\",\n    \"aspect.integrateddynamics.read.integer.block.posz.info\": \"目标的Z坐标\",\n    \"aspect.integrateddynamics.read.nbt.block.tile\": \"方块实体NBT标签\",\n    \"aspect.integrateddynamics.read.nbt.block.tile.info\": \"目标方块实体的NBT标签\",\n    \"aspect.integrateddynamics.read.block.block\": \"方块\",\n    \"aspect.integrateddynamics.read.block.block.info\": \"目标方块\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation\": \"物品展示框内物品旋转角度\",\n    \"aspect.integrateddynamics.read.integer.entity.itemframerotation.info\": \"物品旋转到位置的索引\",\n    \"aspect.integrateddynamics.read.list.entity.entities\": \"实体\",\n    \"aspect.integrateddynamics.read.list.entity.entities.info\": \"目标位置的实体\",\n    \"aspect.integrateddynamics.read.list.entity.players\": \"玩家\",\n    \"aspect.integrateddynamics.read.list.entity.players.info\": \"当前世界的玩家\",\n    \"aspect.integrateddynamics.read.entity.entity\": \"实体\",\n    \"aspect.integrateddynamics.read.entity.entity.info\": \"目标位置选定的实体\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents\": \"物品展示框\",\n    \"aspect.integrateddynamics.read.itemstack.entity.itemframecontents.info\": \"物品展示框内的物品\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random\": \"随机数\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.random.info\": \"随机整型数\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount\": \"玩家数\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.playercount.info\": \"服务器玩家总数\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime\": \"游戏刻\",\n    \"aspect.integrateddynamics.read.integer.extradimensional.ticktime.info\": \"此服务器平均游戏刻耗时(ms)\",\n    \"aspect.integrateddynamics.read.double.extradimensional.tps.info\": \"当前世界每秒的Tick数\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players\": \"玩家\",\n    \"aspect.integrateddynamics.read.list.extradimensional.players.info\": \"在线玩家列表\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty\": \"水槽空\",\n    \"aspect.integrateddynamics.read.boolean.fluid.empty.info\": \"目标水槽是否为空\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full\": \"水槽满\",\n    \"aspect.integrateddynamics.read.boolean.fluid.full.info\": \"目标水槽是否装满\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty\": \"水槽非空\",\n    \"aspect.integrateddynamics.read.boolean.fluid.nonempty.info\": \"是否水槽中有流体\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable\": \"能否存储\",\n    \"aspect.integrateddynamics.read.boolean.fluid.applicable.info\": \"否能储存流体\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks\": \"水槽量\",\n    \"aspect.integrateddynamics.read.integer.fluid.tanks.info\": \"目标位置找到的水槽总量\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio\": \"水槽装满程度\",\n    \"aspect.integrateddynamics.read.double.fluid.fillratio.info\": \"当前位置流体量除以容量\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks\": \"水槽流体\",\n    \"aspect.integrateddynamics.read.list.fluid.fluidstacks.info\": \"所有水槽中的流体列表\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities\": \"水槽容量\",\n    \"aspect.integrateddynamics.read.list.fluid.capacities.info\": \"所有水槽容量列表\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid\": \"水槽流体类别\",\n    \"aspect.integrateddynamics.read.fluidstack.fluid.info\": \"目标水槽中的流体类别\",\n    \"aspect.integrateddynamics.read.fluidstack.block\": \"流体方块\",\n    \"aspect.integrateddynamics.read.fluidstack.block.info\": \"指定方块中的流体类别\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full\": \"物品容器满\",\n    \"aspect.integrateddynamics.read.boolean.inventory.full.info\": \"是否没有空槽\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty\": \"物品容器空\",\n    \"aspect.integrateddynamics.read.boolean.inventory.empty.info\": \"是否均为空槽\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty\": \"物品容器非空\",\n    \"aspect.integrateddynamics.read.boolean.inventory.nonempty.info\": \"是否有物品\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable\": \"能否存储\",\n    \"aspect.integrateddynamics.read.boolean.inventory.applicable.info\": \"能否存储物品\",\n    \"aspect.integrateddynamics.read.integer.inventory.count\": \"物品总数\",\n    \"aspect.integrateddynamics.read.integer.inventory.count.info\": \"获取物品总数\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot\": \"物品栈大小\",\n    \"aspect.integrateddynamics.read.integer.inventory.countslot.info\": \"获取当前物品槽物品栈大小\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots\": \"槽数\",\n    \"aspect.integrateddynamics.read.integer.inventory.slots.info\": \"物品槽总数\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled\": \"装有物品的槽数\",\n    \"aspect.integrateddynamics.read.integer.inventory.slotsfilled.info\": \"有物品的槽数\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio\": \"装满度\",\n    \"aspect.integrateddynamics.read.double.inventory.fillratio.info\": \"非空槽数除以总槽数\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks\": \"物品\",\n    \"aspect.integrateddynamics.read.list.inventory.itemstacks.info\": \"获取物品容器中物品列表\",\n    \"aspect.integrateddynamics.read.itemstack.inventory\": \"槽中物品\",\n    \"aspect.integrateddynamics.read.itemstack.inventory.info\": \"获取选定槽中物品\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker\": \"是否机器\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworker.info\": \"目标是否为可工作的机器\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork\": \"是否有工作\",\n    \"aspect.integrateddynamics.read.boolean.machine.haswork.info\": \"目标是否有工作\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork\": \"能否工作\",\n    \"aspect.integrateddynamics.read.boolean.machine.canwork.info\": \"目标当前状态能否开始工作\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking\": \"是否在工作\",\n    \"aspect.integrateddynamics.read.boolean.machine.isworking.info\": \"目标是否在工作\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature\": \"有无温度\",\n    \"aspect.integrateddynamics.read.boolean.temperature.istemperature.info\": \"目标是否有指定的温度\",\n    \"aspect.integrateddynamics.read.double.temperature.temperature\": \"温度\",\n    \"aspect.integrateddynamics.read.double.temperature.maxtemperature\": \"最大温度\",\n    \"aspect.integrateddynamics.read.double.temperature.mintemperature\": \"最小温度\",\n    \"aspect.integrateddynamics.read.double.temperature.defaulttemperature\": \"默认温度\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable\": \"是否联网\",\n    \"aspect.integrateddynamics.read.boolean.network.applicable.info\": \"目标是否接入网络\",\n    \"aspect.integrateddynamics.read.integer.network.elementcount\": \"元素数\",\n    \"aspect.integrateddynamics.read.integer.network.energy.batterycount\": \"能量电池\",\n    \"aspect.integrateddynamics.read.integer.network.energy.consumptionrate\": \"能源使用率\",\n    \"aspect.integrateddynamics.read.any.network.value\": \"公开值\",\n    \"aspect.integrateddynamics.read.any.network.value.info\": \"目标公开的值\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high\": \"高红石信号\",\n    \"aspect.integrateddynamics.read.boolean.redstone.high.info\": \"红石信号最强\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low\": \"无红石信号\",\n    \"aspect.integrateddynamics.read.boolean.redstone.low.info\": \"无红石信号\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow\": \"有红石信号\",\n    \"aspect.integrateddynamics.read.boolean.redstone.nonlow.info\": \"是否有红石信号\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock\": \"红石时钟\",\n    \"aspect.integrateddynamics.read.boolean.redstone.clock.info\": \"可配置长度和间隔的红石脉冲\",\n    \"aspect.integrateddynamics.read.integer.redstone.value\": \"红石信号等级\",\n    \"aspect.integrateddynamics.read.integer.redstone.value.info\": \"获取精确红石信号等级\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator\": \"比较器\",\n    \"aspect.integrateddynamics.read.integer.redstone.comparator.info\": \"获取比较器输出值\",\n    \"aspect.integrateddynamics.write.boolean.redstone\": \"红石信号输出\",\n    \"aspect.integrateddynamics.write.boolean.redstone.info\": \"如果真,输出最强红石信号\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse\": \"红石脉冲\",\n    \"aspect.integrateddynamics.write.boolean.redstone.pulse.info\": \"当指定值发生改变时,输出一个指定强度的红石脉冲.\",\n    \"aspect.integrateddynamics.write.integer.redstone\": \"红石信号输出\",\n    \"aspect.integrateddynamics.write.integer.redstone.info\": \"将数值输出为对应红石信号强度\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse\": \"红石脉冲\",\n    \"aspect.integrateddynamics.write.integer.redstone.pulse.info\": \"当指定值发生改变时,输出一个指定强度的红石脉冲.\",\n    \"aspect.integrateddynamics.read.boolean.world.entity\": \"有实体\",\n    \"aspect.integrateddynamics.read.boolean.world.entity.info\": \"目标位置是否有实体\",\n    \"aspect.integrateddynamics.read.boolean.world.mob\": \"有怪物\",\n    \"aspect.integrateddynamics.read.boolean.world.mob.info\": \"目标位置是否有怪物\",\n    \"aspect.integrateddynamics.read.boolean.world.animal\": \"有动物\",\n    \"aspect.integrateddynamics.read.boolean.world.animal.info\": \"目标位置是否有动物\",\n    \"aspect.integrateddynamics.read.boolean.world.player\": \"有玩家\",\n    \"aspect.integrateddynamics.read.boolean.world.player.info\": \"目标位置是否有玩家\",\n    \"aspect.integrateddynamics.read.boolean.world.item\": \"有物品\",\n    \"aspect.integrateddynamics.read.boolean.world.item.info\": \"目标位置是否有掉落物\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear\": \"晴天\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.clear.info\": \"当前世界是否晴天\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.raining\": \"雨天\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder\": \"雷雨天\",\n    \"aspect.integrateddynamics.read.boolean.world.weather.thunder.info\": \"当前世界是否雷雨天\",\n    \"aspect.integrateddynamics.read.boolean.world.isday\": \"白天\",\n    \"aspect.integrateddynamics.read.boolean.world.isday.info\": \"当前世界是否白天\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight\": \"夜晚\",\n    \"aspect.integrateddynamics.read.boolean.world.isnight.info\": \"当前世界是否夜晚\",\n    \"aspect.integrateddynamics.read.integer.world.entity\": \"实体数\",\n    \"aspect.integrateddynamics.read.integer.world.entity.info\": \"当前空间实体总数\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown\": \"雨停\",\n    \"aspect.integrateddynamics.read.integer.world.raincountdown.info\": \"下雨剩余时间\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime\": \"游戏刻\",\n    \"aspect.integrateddynamics.read.integer.world.ticktime.info\": \"此世界平均游戏刻耗时(ms)\",\n    \"aspect.integrateddynamics.read.integer.world.daytime\": \"当前时间\",\n    \"aspect.integrateddynamics.read.integer.world.daytime.info\": \"指定世界的当日时间\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel\": \"光亮等级\",\n    \"aspect.integrateddynamics.read.integer.world.lightlevel.info\": \"当前光亮等级\",\n    \"aspect.integrateddynamics.read.integer.world.players\": \"玩家数\",\n    \"aspect.integrateddynamics.read.integer.world.players.info\": \"当前世界玩家数\",\n    \"aspect.integrateddynamics.read.double.world.tps.info\": \"这个世界中每秒的Tick数\",\n    \"aspect.integrateddynamics.read.long.world.time\": \"每日时间\",\n    \"aspect.integrateddynamics.read.long.world.time.info\": \"世界每日时间\",\n    \"aspect.integrateddynamics.read.long.world.totaltime\": \"总时间\",\n    \"aspect.integrateddynamics.read.long.world.totaltime.info\": \"世界总时间\",\n    \"aspect.integrateddynamics.read.string.world.worldname\": \"世界名\",\n    \"aspect.integrateddynamics.read.string.world.worldname.info\": \"当前世界名\",\n    \"aspect.integrateddynamics.read.list.world.players\": \"玩家\",\n    \"aspect.integrateddynamics.read.list.world.players.info\": \"当前世界玩家\",\n    \"aspect.aspecttypes.integrateddynamics.integer.tankid\": \"水槽ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.slotid\": \"插槽ID\",\n    \"aspect.aspecttypes.integrateddynamics.integer.listindex\": \"列出元素\",\n    \"aspect.aspecttypes.integrateddynamics.integer.range\": \"范围\",\n    \"aspect.aspecttypes.integrateddynamics.integer.channel\": \"频道\",\n    \"aspect.aspecttypes.integrateddynamics.integer.interval\": \"脉冲间隔\",\n    \"aspect.aspecttypes.integrateddynamics.integer.length\": \"脉冲长度\",\n    \"aspect.aspecttypes.integrateddynamics.integer.offset\": \"脉冲时间偏移量\",\n    \"aspect.aspecttypes.integrateddynamics.double.volume\": \"音量\",\n    \"aspect.aspecttypes.integrateddynamics.double.frequency\": \"频率\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_x\": \"X轴偏移\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_y\": \"Y轴偏移\",\n    \"aspect.aspecttypes.integrateddynamics.double.offset_z\": \"Z轴偏移\",\n    \"aspect.aspecttypes.integrateddynamics.integer.particles\": \"粒子数目\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_x\": \"X轴放大\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_y\": \"Y轴放大\",\n    \"aspect.aspecttypes.integrateddynamics.double.spread_z\": \"Z轴放大\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power\": \"高能\",\n    \"aspect.aspecttypes.integrateddynamics.boolean.strong_power.info\": \"如果为true则模拟红石火把的输出\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value\": \"脉冲强度\",\n    \"aspect.aspecttypes.integrateddynamics.integer.pulse_emit_value.info\": \"需要发出的脉冲的强度.\",\n    \"operator.integrateddynamics\": \"Operator(运算符)\",\n    \"operator.integrateddynamics.applied.operator_name\": \"%s [%s]\",\n    \"operator.integrateddynamics.applied.type\": \"§应用于: §r%s\",\n    \"operator.integrateddynamics.tooltip.operator_name\": \"§e运算符: §r%s (%s)\",\n    \"operator.integrateddynamics.tooltip.operator_category\": \"§e分类: §r%s\",\n    \"operator.integrateddynamics.tooltip.variable_ids\": \"§e§o变量ID表: §r§o%s\",\n    \"operator.integrateddynamics.tooltip.input_type_name\": \"§e输入值类型 %s: §r%s\",\n    \"operator.integrateddynamics.tooltip.output_type_name\": \"§e输出值类型: §r%s\",\n    \"operator.integrateddynamics.error.wrong_input_length\": \"运算符%s接收长度为%3$s的输入而不是%2$s\",\n    \"operator.integrateddynamics.error.null_type\": \"运算符%s在%s接收到null\",\n    \"operator.integrateddynamics.error.wrong_type\": \"运算符%s在%3$s接收到了%2$s类型而不是%s类型\",\n    \"operator.integrateddynamics.error.wrong_currying_type\": \"运算符 %s 接收到了一个位于 %s  %s 类型输入 当类型 %s 预计会执行Currying(柯里化)。\",\n    \"operator.integrateddynamics.error.wrong_predicate\": \"运算符%s接收到了输入运算符输出的类型%s而不是%s\",\n    \"operator.integrateddynamics.error.illegal_property\": \"该运算符被认为将输出%1$s,但运算符%3$s收到了%2$s\",\n    \"operator.integrateddynamics.error.variable_not_in_network\": \"当前网络中找不到ID为%s的变量\",\n    \"operator.integrateddynamics.error.cyclic_reference\": \"ID为%s的变量引用了自己,这会导致无限递归\",\n    \"operator.integrateddynamics.error.wrong_input_length_virtual\": \"运算符%s 接收到了长度为%s 的运算符%s ,但是长度%s 是必须的.\",\n    \"operator.integrateddynamics.logical\": \"逻辑\",\n    \"operator.integrateddynamics.logical.basename\": \"逻辑%s\",\n    \"operator.integrateddynamics.logical.and\": \"与\",\n    \"operator.integrateddynamics.logical.or\": \"或\",\n    \"operator.integrateddynamics.logical.not\": \"非\",\n    \"operator.integrateddynamics.logical.nand\": \"与非\",\n    \"operator.integrateddynamics.logical.nor\": \"或非\",\n    \"operator.integrateddynamics.arithmetic\": \"运算\",\n    \"operator.integrateddynamics.arithmetic.basename\": \"%s运算符\",\n    \"operator.integrateddynamics.arithmetic.addition\": \"加\",\n    \"operator.integrateddynamics.arithmetic.subtraction\": \"减\",\n    \"operator.integrateddynamics.arithmetic.multiplication\": \"乘\",\n    \"operator.integrateddynamics.arithmetic.division\": \"除\",\n    \"operator.integrateddynamics.arithmetic.maximum\": \"最大\",\n    \"operator.integrateddynamics.arithmetic.maximum.info\": \"取两个数值中的最大值\",\n    \"operator.integrateddynamics.arithmetic.minimum\": \"最小\",\n    \"operator.integrateddynamics.arithmetic.minimum.info\": \"取两个数值中的最小值\",\n    \"operator.integrateddynamics.arithmetic.modulus\": \"取模\",\n    \"operator.integrateddynamics.arithmetic.increment\": \"递增\",\n    \"operator.integrateddynamics.arithmetic.decrement\": \"递减\",\n    \"operator.integrateddynamics.integer\": \"整型\",\n    \"operator.integrateddynamics.integer.basename\": \"整型%s运算\",\n    \"operator.integrateddynamics.integer.modulus\": \"取模\",\n    \"operator.integrateddynamics.integer.increment\": \"递增\",\n    \"operator.integrateddynamics.integer.decrement\": \"递减\",\n    \"operator.integrateddynamics.relational\": \"关系运算\",\n    \"operator.integrateddynamics.relational.basename\": \"关系%s运算\",\n    \"operator.integrateddynamics.relational.equals\": \"等于\",\n    \"operator.integrateddynamics.relational.gt\": \"大于\",\n    \"operator.integrateddynamics.relational.lt\": \"小于\",\n    \"operator.integrateddynamics.relational.notequals\": \"不等\",\n    \"operator.integrateddynamics.relational.ge\": \"大于等于\",\n    \"operator.integrateddynamics.relational.le\": \"小于等于\",\n    \"operator.integrateddynamics.binary\": \"二进制运算\",\n    \"operator.integrateddynamics.binary.basename\": \"二进制%s运算\",\n    \"operator.integrateddynamics.binary.and\": \"按位与\",\n    \"operator.integrateddynamics.binary.or\": \"按位同或\",\n    \"operator.integrateddynamics.binary.xor\": \"按位异或\",\n    \"operator.integrateddynamics.binary.complement\": \"补码\",\n    \"operator.integrateddynamics.binary.rshift\": \"右移\",\n    \"operator.integrateddynamics.binary.rshift.info\": \"右移补一\",\n    \"operator.integrateddynamics.binary.lshift\": \"左移\",\n    \"operator.integrateddynamics.binary.rzshift\": \"右补零\",\n    \"operator.integrateddynamics.binary.rzshift.info\": \"右移补零\",\n    \"operator.integrateddynamics.string\": \"字符串\",\n    \"operator.integrateddynamics.string.basename\": \"字符串%s\",\n    \"operator.integrateddynamics.string.length\": \"长度\",\n    \"operator.integrateddynamics.string.length.info\": \"获取指定字符串的长度\",\n    \"operator.integrateddynamics.string.concat\": \"连接\",\n    \"operator.integrateddynamics.string.concat.info\": \"连接两个字符串\",\n    \"operator.integrateddynamics.string.contains\": \"字符串包含\",\n    \"operator.integrateddynamics.string.contains.info\": \"判断指定的子字符串是否包含在指定的字符串中.\",\n    \"operator.integrateddynamics.string.contains_regex\": \"正则包含\",\n    \"operator.integrateddynamics.string.contains_regex.info\": \"判断指定的正则表达式是否能在指定字符串中找到匹配.\",\n    \"operator.integrateddynamics.string.matches_regex\": \"正则匹配\",\n    \"operator.integrateddynamics.string.matches_regex.info\": \"判断指定的正则表达式是否匹配指定的字符串.\",\n    \"operator.integrateddynamics.string.index_of\": \"索引\",\n    \"operator.integrateddynamics.string.index_of.info\": \"获取指定的子字符串在指定字符串中第一次出现的位置.\",\n    \"operator.integrateddynamics.string.index_of_regex\": \"正则索引\",\n    \"operator.integrateddynamics.string.index_of_regex.info\": \"获取指定的正则表达式在指定字符串中第一次匹配到的位置.\",\n    \"operator.integrateddynamics.string.starts_with\": \"以其开头\",\n    \"operator.integrateddynamics.string.starts_with.info\": \"判断指定的子字符串是否是指定的字符串的开头.\",\n    \"operator.integrateddynamics.string.ends_with\": \"以其结尾\",\n    \"operator.integrateddynamics.string.ends_with.info\": \"判断指定的子字符串是否是指定字符串的结尾.\",\n    \"operator.integrateddynamics.string.split_on\": \"拆分\",\n    \"operator.integrateddynamics.string.split_on.info\": \"用指定的分隔符(文本)拆分字符串,并返回拆分后的列表.\",\n    \"operator.integrateddynamics.string.split_on_regex\": \"正则拆分\",\n    \"operator.integrateddynamics.string.split_on_regex.info\": \"以指定的分隔符(正则表达式)拆分字符串,并返回拆分后的列表.\",\n    \"operator.integrateddynamics.string.substring\": \"子字符串\",\n    \"operator.integrateddynamics.string.substring.info\": \"从指定的字符串中返回从指定的起始索引(包含文字)到指定的末尾索引(排除)之间的字符串片段.\",\n    \"operator.integrateddynamics.string.regex_group\": \"正则匹配组\",\n    \"operator.integrateddynamics.string.regex_groups\": \"所有正则匹配组\",\n    \"operator.integrateddynamics.string.regex_scan\": \"正则扫描\",\n    \"operator.integrateddynamics.string.replace\": \"替换\",\n    \"operator.integrateddynamics.string.replace.info\": \"在指定的字符串中找到所有指定的搜索词的匹配,并用指定的字符串替换它们.\",\n    \"operator.integrateddynamics.string.replace_regex\": \"正则替换\",\n    \"operator.integrateddynamics.string.replace_regex.info\": \"在指定的字符串中找到所有指定的正则表达式的匹配,并用指定的字符串替换它们.\",\n    \"operator.integrateddynamics.string.name\": \"变量名\",\n    \"operator.integrateddynamics.string.name.info\": \"获取指定变量名或变量值的字符串形式\",\n    \"operator.integrateddynamics.string.unique_name\": \"唯一名称\",\n    \"operator.integrateddynamics.string.unique_name.info\": \"获取指定变量的唯一名称\",\n    \"operator.integrateddynamics.double\": \"双精度浮点型\",\n    \"operator.integrateddynamics.double.basename\": \"双精度浮点型%s\",\n    \"operator.integrateddynamics.list\": \"列表\",\n    \"operator.integrateddynamics.list.basename\": \"列表%s\",\n    \"operator.integrateddynamics.list.length\": \"长度\",\n    \"operator.integrateddynamics.list.length.info\": \"指定列表的长度\",\n    \"operator.integrateddynamics.list.empty\": \"空\",\n    \"operator.integrateddynamics.list.empty.info\": \"此列表是否为空\",\n    \"operator.integrateddynamics.list.notempty\": \"非空\",\n    \"operator.integrateddynamics.list.notempty.info\": \"此列表是否非空\",\n    \"operator.integrateddynamics.list.get\": \"获取\",\n    \"operator.integrateddynamics.list.get.info\": \"获取对应下标的列表元素\",\n    \"operator.integrateddynamics.list.get_or_default\": \"获取或默认值\",\n    \"operator.integrateddynamics.list.get_or_default.info\": \"安全的获取列表指定位置的元素,如果元素不可用,则返回默认值\",\n    \"operator.integrateddynamics.list.contains\": \"包含\",\n    \"operator.integrateddynamics.list.contains.info\": \"此列表是否包含指定元素\",\n    \"operator.integrateddynamics.list.contains_p\": \"包含谓词\",\n    \"operator.integrateddynamics.list.contains_p.info\": \"如果列表的至少一个元素对于指定的谓词返回true。\",\n    \"operator.integrateddynamics.list.count\": \"计数\",\n    \"operator.integrateddynamics.list.count.info\": \"在列表中找到指定元素的次数。\",\n    \"operator.integrateddynamics.list.count_p\": \"计数\",\n    \"operator.integrateddynamics.list.count_p.info\": \"指定谓词对于列表中的元素返回true的次数。\",\n    \"operator.integrateddynamics.list.append\": \"附加\",\n    \"operator.integrateddynamics.list.append.info\": \"在指定的列表中附加指定的项目。\",\n    \"operator.integrateddynamics.list.concat\": \"拼接\",\n    \"operator.integrateddynamics.list.lazybuilt\": \"懒惰列表生成器\",\n    \"operator.integrateddynamics.list.lazybuilt.info\": \"使用起始数据和应用于上一个元素的运算符来构建列表以获取下一个元素。\",\n    \"operator.integrateddynamics.list.head\": \"首\",\n    \"operator.integrateddynamics.list.head.info\": \"获取指定列表的第一个元素。\",\n    \"operator.integrateddynamics.list.tail\": \"尾\",\n    \"operator.integrateddynamics.list.tail.info\": \"从列表中删除第一个元素。\",\n    \"operator.integrateddynamics.list.uniq_p\": \"条件唯一\",\n    \"operator.integrateddynamics.list.uniq_p.info\": \"基于指定的比较条件删除列表中所有的重复元素。\",\n    \"operator.integrateddynamics.list.uniq\": \"唯一\",\n    \"operator.integrateddynamics.list.uniq.info\": \"删除列表中所有的重复元素。\",\n    \"operator.integrateddynamics.list.slice\": \"切片\",\n    \"operator.integrateddynamics.list.slice.info\": \"将指定列表的子集从指定索引(包括)移到指定索引(不包括)。\",\n    \"operator.integrateddynamics.block\": \"方块\",\n    \"operator.integrateddynamics.block.basename\": \"方块%s\",\n    \"operator.integrateddynamics.block.opaque\": \"不透明\",\n    \"operator.integrateddynamics.block.opaque.info\": \"指定的方块是否不透明\",\n    \"operator.integrateddynamics.block.itemstack\": \"物品\",\n    \"operator.integrateddynamics.block.itemstack.info\": \"指定方块代表的物品\",\n    \"operator.integrateddynamics.block.mod\": \"方块所属Mod\",\n    \"operator.integrateddynamics.block.mod.info\": \"指定方块对应的Mod名称\",\n    \"operator.integrateddynamics.block.breaksound\": \"方块破坏声\",\n    \"operator.integrateddynamics.block.breaksound.info\": \"指定方块被破坏的声音\",\n    \"operator.integrateddynamics.block.placesound\": \"方块放置声\",\n    \"operator.integrateddynamics.block.placesound.info\": \"指定方块被放置的声音\",\n    \"operator.integrateddynamics.block.stepsound\": \"方块行走声\",\n    \"operator.integrateddynamics.block.stepsound.info\": \"在指定方块上行走的声音\",\n    \"operator.integrateddynamics.block.isshearable\": \"方块可被使用剪刀\",\n    \"operator.integrateddynamics.block.isshearable.info\": \"如果方块可以被使用剪刀\",\n    \"operator.integrateddynamics.block.cansustainplantblock\": \"方块是否可以种植__方块\",\n    \"operator.integrateddynamics.block.cansustainplantblock.info\": \"如果指定方块可以种植指定植物方块\",\n    \"operator.integrateddynamics.block.cansustainplantitem\": \"方块可以种植__物品\",\n    \"operator.integrateddynamics.block.cansustainplantitem.info\": \"如果指定方块可以种植指定植物物品\",\n    \"operator.integrateddynamics.block.plantage\": \"植物方块成熟度\",\n    \"operator.integrateddynamics.block.plantage.info\": \"指定植物方块的成熟度\",\n    \"operator.integrateddynamics.block.blockbyname\": \"通过名称检索方块\",\n    \"operator.integrateddynamics.itemstack\": \"物品\",\n    \"operator.integrateddynamics.itemstack.basename\": \"物品%s\",\n    \"operator.integrateddynamics.itemstack.size\": \"数量\",\n    \"operator.integrateddynamics.itemstack.size.info\": \"当前物品栈物品数量\",\n    \"operator.integrateddynamics.itemstack.maxsize\": \"数量上限\",\n    \"operator.integrateddynamics.itemstack.maxsize.info\": \"当前物品的堆叠上限\",\n    \"operator.integrateddynamics.itemstack.stackable\": \"可堆叠\",\n    \"operator.integrateddynamics.itemstack.stackable.info\": \"物品能否堆叠两个及以上\",\n    \"operator.integrateddynamics.itemstack.damageable\": \"可损坏\",\n    \"operator.integrateddynamics.itemstack.damageable.info\": \"物品可以损坏\",\n    \"operator.integrateddynamics.itemstack.damage\": \"损害值\",\n    \"operator.integrateddynamics.itemstack.damage.info\": \"当前物品的损害值\",\n    \"operator.integrateddynamics.itemstack.maxdamage\": \"损害值上限\",\n    \"operator.integrateddynamics.itemstack.maxdamage.info\": \"物品损害值上限\",\n    \"operator.integrateddynamics.itemstack.enchanted\": \"已附魔\",\n    \"operator.integrateddynamics.itemstack.enchanted.info\": \"物品是否已经附魔\",\n    \"operator.integrateddynamics.itemstack.enchantable\": \"能否附魔\",\n    \"operator.integrateddynamics.itemstack.enchantable.info\": \"物品能否附魔\",\n    \"operator.integrateddynamics.itemstack.repaircost\": \"修复花费\",\n    \"operator.integrateddynamics.itemstack.repaircost.info\": \"修复此物品的花费\",\n    \"operator.integrateddynamics.itemstack.rarity\": \"稀有度\",\n    \"operator.integrateddynamics.itemstack.rarity.info\": \"物品的稀有度\",\n    \"operator.integrateddynamics.itemstack.strength\": \"挖掘强度比较\",\n    \"operator.integrateddynamics.itemstack.strength.info\": \"该物品的挖掘强度与指定的方块的强度比较\",\n    \"operator.integrateddynamics.itemstack.canharvest\": \"能否采集\",\n    \"operator.integrateddynamics.itemstack.canharvest.info\": \"该物品能否采集指定方块\",\n    \"operator.integrateddynamics.itemstack.block\": \"方块\",\n    \"operator.integrateddynamics.itemstack.block.info\": \"指定物品对应的方块\",\n    \"operator.integrateddynamics.itemstack.isfluidstack\": \"是内有流体的流体容器\",\n    \"operator.integrateddynamics.itemstack.isfluidstack.info\": \"指定物品中有流体\",\n    \"operator.integrateddynamics.itemstack.fluidstack\": \"对应流体\",\n    \"operator.integrateddynamics.itemstack.fluidstack.info\": \"指定物品中对应的流体\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity\": \"流体容器容量\",\n    \"operator.integrateddynamics.itemstack.fluidstackcapacity.info\": \"指定流体容器的容量(mB)\",\n    \"operator.integrateddynamics.itemstack.isnbtequal\": \"物品比较(完全相等)\",\n    \"operator.integrateddynamics.itemstack.isnbtequal.info\": \"比较两个指定物品是否相同,包括NBT\",\n    \"operator.integrateddynamics.itemstack.isitemequalnonbt\": \"物品比较(排除NBT)\",\n    \"operator.integrateddynamics.itemstack.israwitemequal\": \"物品比较(模糊)\",\n    \"operator.integrateddynamics.itemstack.mod\": \"物品所属Mod\",\n    \"operator.integrateddynamics.itemstack.mod.info\": \"指定物品对应的Mod名称\",\n    \"operator.integrateddynamics.itemstack.burntime\": \"燃烧时间\",\n    \"operator.integrateddynamics.itemstack.burntime.info\": \"指定物品的燃烧时间(Tick)\",\n    \"operator.integrateddynamics.itemstack.canburn\": \"可否燃烧\",\n    \"operator.integrateddynamics.itemstack.canburn.info\": \"指定物品是否可用作燃料\",\n    \"operator.integrateddynamics.itemstack.hasinventory\": \"物品具有存储能力\",\n    \"operator.integrateddynamics.itemstack.hasinventory.info\": \"如果物品具有存储能力\",\n    \"operator.integrateddynamics.itemstack.inventorysize\": \"物品存储容量\",\n    \"operator.integrateddynamics.itemstack.inventorysize.info\": \"获取物品处理容器的存储容量\",\n    \"operator.integrateddynamics.itemstack.inventory\": \"物品存储\",\n    \"operator.integrateddynamics.itemstack.inventory.info\": \"获取物品处理容器的库存\",\n    \"operator.integrateddynamics.itemstack.withsize\": \"物品堆叠大小\",\n    \"operator.integrateddynamics.itemstack.withsize.info\": \"设置指定物品的堆叠大小\",\n    \"operator.integrateddynamics.itemstack.itembyname\": \"通过名称检索物品\",\n    \"operator.integrateddynamics.itemstack.itemlistcount\": \"物品列表总数\",\n    \"operator.integrateddynamics.itemstack.itemlistcount.info\": \"获取指定物品在列表中的数量\",\n    \"operator.integrateddynamics.itemstack.nbt\": \"物品NBT标签\",\n    \"operator.integrateddynamics.itemstack.nbt.info\": \"获取指定物品的NBT标签\",\n    \"operator.integrateddynamics.entity\": \"实体\",\n    \"operator.integrateddynamics.entity.basename\": \"实体%s\",\n    \"operator.integrateddynamics.entity.ismob\": \"是否怪物\",\n    \"operator.integrateddynamics.entity.ismob.info\": \"实体是否为怪物\",\n    \"operator.integrateddynamics.entity.isanimal\": \"是否动物\",\n    \"operator.integrateddynamics.entity.isanimal.info\": \"实体是否为动物\",\n    \"operator.integrateddynamics.entity.isitem\": \"是否掉落物\",\n    \"operator.integrateddynamics.entity.isitem.info\": \"实体是否为掉落物\",\n    \"operator.integrateddynamics.entity.isplayer\": \"是否玩家\",\n    \"operator.integrateddynamics.entity.isplayer.info\": \"实体是否为玩家\",\n    \"operator.integrateddynamics.entity.isminecart\": \"是否矿车\",\n    \"operator.integrateddynamics.entity.isminecart.info\": \"实体是否为矿车\",\n    \"operator.integrateddynamics.entity.item\": \"对应物品\",\n    \"operator.integrateddynamics.entity.item.info\": \"掉落物对应物品\",\n    \"operator.integrateddynamics.entity.health\": \"生命值\",\n    \"operator.integrateddynamics.entity.health.info\": \"实体生命值\",\n    \"operator.integrateddynamics.entity.width\": \"宽度\",\n    \"operator.integrateddynamics.entity.width.info\": \"实体宽度\",\n    \"operator.integrateddynamics.entity.height\": \"高度\",\n    \"operator.integrateddynamics.entity.height.info\": \"实体高度\",\n    \"operator.integrateddynamics.entity.isburning\": \"是否燃烧\",\n    \"operator.integrateddynamics.entity.isburning.info\": \"实体是否在燃烧\",\n    \"operator.integrateddynamics.entity.iswet\": \"是否在水中\",\n    \"operator.integrateddynamics.entity.iswet.info\": \"实体是否在水中\",\n    \"operator.integrateddynamics.entity.iseating\": \"是否进食\",\n    \"operator.integrateddynamics.entity.iseating.info\": \"实体是否在进食\",\n    \"operator.integrateddynamics.entity.armorinventory\": \"盔甲栏\",\n    \"operator.integrateddynamics.entity.armorinventory.info\": \"实体作为盔甲携带的物品列表\",\n    \"operator.integrateddynamics.entity.inventory\": \"玩家物品栏\",\n    \"operator.integrateddynamics.entity.inventory.info\": \"玩家携带的物品列表\",\n    \"operator.integrateddynamics.entity.mod.info\": \"所给实体对应的mod\",\n    \"operator.integrateddynamics.entity.targetblock\": \"目标方块\",\n    \"operator.integrateddynamics.entity.targetblock.info\": \"实体正在看的方块\",\n    \"operator.integrateddynamics.entity.targetentity\": \"目标实体\",\n    \"operator.integrateddynamics.entity.targetentity.info\": \"实体正在看的实体\",\n    \"operator.integrateddynamics.entity.hasguiopen\": \"GUI是否打开\",\n    \"operator.integrateddynamics.entity.hasguiopen.info\": \"指定玩家是否开着GUI\",\n    \"operator.integrateddynamics.entity.helditem\": \"手持物品\",\n    \"operator.integrateddynamics.entity.helditem.info\": \"实体正手持的物品\",\n    \"operator.integrateddynamics.entity.helditemoffhand\": \"次要手持物品\",\n    \"operator.integrateddynamics.entity.mounted\": \"骑行实体\",\n    \"operator.integrateddynamics.entity.mounted.info\": \"骑在实体上的实体\",\n    \"operator.integrateddynamics.entity.itemframecontents\": \"物品展示框内容\",\n    \"operator.integrateddynamics.entity.itemframecontents.info\": \"指定物品展示框的内容\",\n    \"operator.integrateddynamics.entity.itemframerotation\": \"物品展示框物品选择\",\n    \"operator.integrateddynamics.entity.itemframerotation.info\": \"指定物品展示框内物品的旋转情况\",\n    \"operator.integrateddynamics.entity.hurtsound\": \"实体受伤声\",\n    \"operator.integrateddynamics.entity.hurtsound.info\": \"指定实体受伤的声音\",\n    \"operator.integrateddynamics.entity.deathsound\": \"实体死亡声\",\n    \"operator.integrateddynamics.entity.deathsound.info\": \"指定实体死亡的声音\",\n    \"operator.integrateddynamics.entity.age\": \"实体年龄\",\n    \"operator.integrateddynamics.entity.age.info\": \"指定实体的年龄.\",\n    \"operator.integrateddynamics.entity.ischild\": \"实体是幼体\",\n    \"operator.integrateddynamics.entity.ischild.info\": \"如果指定实体是幼体.\",\n    \"operator.integrateddynamics.entity.canbreed\": \"实体可以繁殖\",\n    \"operator.integrateddynamics.entity.canbreed.info\": \"如果指定实体已经准备好繁殖.\",\n    \"operator.integrateddynamics.entity.isinlove\": \"实体处于求爱模式\",\n    \"operator.integrateddynamics.entity.isinlove.info\": \"如果指定实体已经进入求爱模式并准备好繁殖.\",\n    \"operator.integrateddynamics.entity.canbreedwith\": \"实体可以用__喂养\",\n    \"operator.integrateddynamics.entity.isshearable\": \"实体可以被使用剪刀\",\n    \"operator.integrateddynamics.entity.isshearable.info\": \"如果指定实体可以被使用剪刀\",\n    \"operator.integrateddynamics.entity.nbt\": \"实体NBT标签\",\n    \"operator.integrateddynamics.entity.nbt.info\": \"获取指定实体的NBT标签.\",\n    \"operator.integrateddynamics.entity.entitytype\": \"实体种类\",\n    \"operator.integrateddynamics.entity.entitytype.info\": \"实体种类的名称.\",\n    \"operator.integrateddynamics.entity.entityitems\": \"实体所有物\",\n    \"operator.integrateddynamics.entity.entityitems.info\": \"实体中包含的物品.\",\n    \"operator.integrateddynamics.entity.entityfluids\": \"实体流体\",\n    \"operator.integrateddynamics.entity.entityfluids.info\": \"实体中包含的流体.\",\n    \"operator.integrateddynamics.entity.entityenergystored\": \"实体存储能量\",\n    \"operator.integrateddynamics.entity.entityenergystored.info\": \"实体中存储的能量.\",\n    \"operator.integrateddynamics.entity.entityenergycapacity\": \"实体能量容量\",\n    \"operator.integrateddynamics.entity.entityenergycapacity.info\": \"实体可存储能量的上限.\",\n    \"operator.integrateddynamics.fluidstack\": \"流体\",\n    \"operator.integrateddynamics.fluidstack.basename\": \"流体%s\",\n    \"operator.integrateddynamics.fluidstack.amount\": \"总量\",\n    \"operator.integrateddynamics.fluidstack.amount.info\": \"流体总量(mb)\",\n    \"operator.integrateddynamics.fluidstack.block\": \"方块\",\n    \"operator.integrateddynamics.fluidstack.block.info\": \"流体对应方块\",\n    \"operator.integrateddynamics.fluidstack.density\": \"密度\",\n    \"operator.integrateddynamics.fluidstack.density.info\": \"流体密度\",\n    \"operator.integrateddynamics.fluidstack.viscosity\": \"流动性\",\n    \"operator.integrateddynamics.fluidstack.viscosity.info\": \"液体流动性\",\n    \"operator.integrateddynamics.fluidstack.rarity\": \"稀有度\",\n    \"operator.integrateddynamics.fluidstack.rarity.info\": \"流体稀有度\",\n    \"operator.integrateddynamics.fluidstack.israwfluidequal\": \"相同\",\n    \"operator.integrateddynamics.fluidstack.mod.info\": \"流体对应的Mod\",\n    \"operator.integrateddynamics.fluidstack.nbt\": \"流体NBT标签\",\n    \"operator.integrateddynamics.fluidstack.nbt.info\": \"获取指定流体的NBT标签\",\n    \"operator.integrateddynamics.nbt\": \"NBT标签\",\n    \"operator.integrateddynamics.operator\": \"运算符\",\n    \"operator.integrateddynamics.operator.basename\": \"%s运算符\",\n    \"operator.integrateddynamics.operator.apply\": \"一元应用\",\n    \"operator.integrateddynamics.operator.apply.info\": \"用指定的值执行运算符\",\n    \"operator.integrateddynamics.operator.apply2\": \"二元应用\",\n    \"operator.integrateddynamics.operator.apply2.info\": \"使用指定的运算符计算两个指定的值\",\n    \"operator.integrateddynamics.operator.apply3\": \"三元\",\n    \"operator.integrateddynamics.operator.map\": \"遍历映射\",\n    \"operator.integrateddynamics.operator.map.info\": \"用列表中的参数依次执行运算符 输出新的映射值列表\",\n    \"operator.integrateddynamics.operator.filter\": \"过滤\",\n    \"operator.integrateddynamics.operator.filter.info\": \"用指定的断言过滤元素列表\",\n    \"operator.integrateddynamics.operator.conjunction\": \"合取\",\n    \"operator.integrateddynamics.operator.conjunction.info\": \"取两个谓词的合取式\",\n    \"operator.integrateddynamics.operator.disjunction\": \"析取\",\n    \"operator.integrateddynamics.operator.disjunction.info\": \"取消两个谓词的分离。\",\n    \"operator.integrateddynamics.operator.negation\": \"反转\",\n    \"operator.integrateddynamics.operator.negation.info\": \"取断言的相反值\",\n    \"operator.integrateddynamics.operator.pipe\": \"管道\",\n    \"operator.integrateddynamics.operator.pipe.info\": \"创建一个新的运算符,将输入的第一个操作的输出值传递给第二个运算符\",\n    \"operator.integrateddynamics.operator.pipe2\": \"管道二元\",\n    \"operator.integrateddynamics.operator.pipe2.info\": \"创建一个新的运算符，将输入的第一个和第二个运算符的输出传递到第三个运算符.\",\n    \"operator.integrateddynamics.operator.flip\": \"翻转\",\n    \"operator.integrateddynamics.operator.flip.info\": \"用两个输入翻转运算符的输入参数。\",\n    \"operator.integrateddynamics.virtual.flipped\": \"翻转操作\",\n    \"operator.integrateddynamics.operator.reduce\": \"归约\",\n    \"operator.integrateddynamics.operator.reduce.info\": \"将指定的运算符应用于列表的所有元素，以将列表归约（reduce）为一个值。\",\n    \"operator.integrateddynamics.operator.reduce1\": \"归约1\",\n    \"operator.integrateddynamics.operator.reduce1.info\": \"将指定的运算符应用于列表的所有元素，以将列表归约（reduce）为一个值。reduce1(op,list)等同于reduce(op,tail(list),head(list))。\",\n    \"operator.integrateddynamics.general\": \"全局\",\n    \"operator.integrateddynamics.general.basename\": \"全局%s\",\n    \"operator.integrateddynamics.general.choice\": \"选择\",\n    \"operator.integrateddynamics.general.choice.info\": \"如果第一个值为真,取第二个值,否则取第三个值\\\\n第二个和第三个的类型必须相同\",\n    \"operator.integrateddynamics.general.identity\": \"复制\",\n    \"operator.integrateddynamics.general.identity.info\": \"复制输入值\",\n    \"operator.integrateddynamics.general.constant\": \"常量\",\n    \"operator.integrateddynamics.general.constant.info\": \"第一次输入的值的副本.\",\n    \"operator.integrateddynamics.general.isnull\": \"空\",\n    \"operator.integrateddynamics.general.isnull.info\": \"指定的值是否为空\",\n    \"operator.integrateddynamics.general.isnotnull\": \"非空\",\n    \"operator.integrateddynamics.general.isnotnull.info\": \"指定的值是否非空\",\n    \"operator.integrateddynamics.virtual\": \"虚拟运算符\",\n    \"operator.integrateddynamics.virtual.basename\": \"虚拟运算符%s\",\n    \"operator.integrateddynamics.virtual.p_conjunction\": \"谓词合取式\",\n    \"operator.integrateddynamics.virtual.p_conjunction.info\": \"两个谓词的合取式\",\n    \"operator.integrateddynamics.virtual.p_disjunction\": \"谓词分离\",\n    \"operator.integrateddynamics.virtual.p_disjunction.info\": \"两个谓词的分离\",\n    \"operator.integrateddynamics.virtual.p_negation\": \"谓词否定\",\n    \"operator.integrateddynamics.virtual.p_negation.info\": \"谓词的分离\",\n    \"operator.integrateddynamics.virtual.piped\": \"管道操作\",\n    \"operator.integrateddynamics.virtual.piped.info\": \"运算符链\",\n    \"operator.integrateddynamics.virtual.piped2\": \"管道二元操作\",\n    \"operator.integrateddynamics.cast\": \"转换\",\n    \"operator.integrateddynamics.cast.basename\": \"类型转换\",\n    \"operator.integrateddynamics.cast.tooltip\": \"将类型%s转换为类型%s\",\n    \"info_book.integrateddynamics.section.main\": \"动态联合手册\",\n    \"info_book.integrateddynamics.tag_index\": \"&o索引&r\",\n    \"info_book.integrateddynamics.introduction\": \"介绍\",\n    \"info_book.integrateddynamics.introduction.text1\": \"长久以来, 自动化的自由度太有限了. &1动态联合&0致力于让你的逻辑系统更加的智能化。 它为你提供了其最具有代表性的沉浸逻辑编程体验，并且支持大部分&lMinecraft&r和Mod里的要素。\",\n    \"info_book.integrateddynamics.introduction.text2\": \"红石电路一直以来是最基础的, 也是一种简单易懂的途径来实现自动化。 然而归根结底，它并不方便。如果想要构建一个高复杂度的自动化网络,红石系统将会做的非常的大，并且难以设计。更要命的是，如果你不小心弄泼了一桶水，它也许能会毁掉你的整个红石系统。\",\n    \"info_book.integrateddynamics.introduction.text4\": \"你可以通过点击手册底部的箭头来翻页阅读, 按住shift左机可一次性翻过整个当前部分。在主目录中，您将看到一个内容一览表，其中将包含到各个子目录的超链接，以便你更快的查阅。 当你遇到一个配方时,如果配方里含有本书中解释的项目可以通过点击它来进行快速的查阅。你会注意到本书的左上方也有一个按钮,它是用于返回上级目录的。右上角的按钮能让你返回到先前访问过的页面。\",\n    \"info_book.integrateddynamics.tutorials\": \"教程\",\n    \"info_book.integrateddynamics.tutorials.introduction\": \"介绍\",\n    \"info_book.integrateddynamics.tutorials.introduction.text1\": \"该目录包含了几个教程部分的内容,它将帮助你了解本mod的基础部分。另外,当你达成每个教学部分的成就后你会获得一些&l奖励&r。\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics\": \"门瑞欧基础\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text1\": \"开始该mod之前, 你需要找到一些&l门瑞欧树&r，在&l门勒格林&r能很容易的找到。\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text3\": \"如果想要重复此步骤,给予&l挤压机&r一个红石脉冲，便可将它重置。\",\n    \"info_book.integrateddynamics.tutorials.menrilBasics.text4\": \"现在, 你还可以制作更易于自动化,同时消耗也更高的机器: &l电动挤压机&r和&l电动烘干池&r.\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations\": \"网络基础\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text1\": \"本mod的主要目的就是构建一个可控制的自动化网络。通多使用&l逻辑线缆&r来实现。试着用10根这样的逻辑线缆来搭建一个网络吧~\",\n    \"info_book.integrateddynamics.tutorials.networkFoundations.text2\": \"下一步需要做一些&l变量卡&r和&l变量转换器&r。 &l变量卡&r用来存储网络数据的引用。&l变量装换器&r为可以放置在&l逻辑线缆&r上的元件制作元素，以便读取和写入这些数据。\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents\": \"基本网络元件\",\n    \"info_book.integrateddynamics.tutorials.basicNetworkComponents.text1\": \"为&l变量卡&r创建特定数据的一种方法是,通过&l读取器&r读取它的数据。去做一些基础读取器吧。然后, 再做一个&l显示面板&r,他能够方便的显示&l变量卡&r的数据。\",\n    \"info_book.integrateddynamics.tutorials.aspects\": \"设置面\",\n    \"info_book.integrateddynamics.tutorials.aspects.text1\": \"举个简单的例子, 我们来读取一个红石的数据并将它显示出来\",\n    \"info_book.integrateddynamics.tutorials.aspects.text3\": \"接下来, 右击打开&l红石读取器&r的GUI, 将&l变量卡&r插入&6Integer&0 &l红石数据&r设置面, 来把目标的红石数据绑定到&l变量卡&r上。\",\n    \"info_book.integrateddynamics.tutorials.aspects.text5\": \"下一步, 做一个&l红石输出器&r 并让他输出一个最大的红石数据\",\n    \"info_book.integrateddynamics.tutorials.logicOperations\": \"逻辑运算符\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text1\": \"当读取器上显示的数据可用时,开始将特定的数据与其他的数据通过&2运算符&0连接，事情将会变得有趣起来。所有的&2运算符&0可以在&l逻辑编程器&r中找到。\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text2\": \"首先你需要做一个&l逻辑编程器&r, 它同时拥有物品和方块两种存在形式。打开GUI可在列表中查看所有的&l运算符&r.\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text3\": \"接下来，搜索&6Integer&0运算符,点击它。它将会打开一个需要你输入数字的窗口。输入你想要的数字后, 插入一个空白的&l变量卡&r来绑定你刚才设置的&6Integer&0。设置完后，重复此运算来设置另一个不同的&6Integer&0变量卡。\",\n    \"info_book.integrateddynamics.tutorials.logicOperations.text4\": \"最后,搜索&lAddition&r运算符并打开它。他会让你添加两个&e数字&0。插入2张&6Integer&0 &l变量卡&r到这些插槽内, 将它绑定到一张新的&l变量卡&r上。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents\": \"高级网络元件\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1\": \"现在你已经熟悉了基础的逻辑部件,我们来看一看更多高级的网络元件:  &l复制器&r 和 &l代理器&r。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2\": \"你肯定记得, &l逻辑编程器&r能让你创建变量的&o静态&r数据, 你可以通过&o动态&r数据评测创建更多复杂的变量。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3\": \"&l复制器&r是用来将&o动态&r数据转换成&o静态&r数据的。插入&o动态&r &l变量卡&r 到 &l复制器&r顶部的插槽内。每个插入左槽的&l变量卡&r , 将会复制顶槽变量卡当前的数据。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4\": \"&l代理器&r能让你为变量添加一个额外的间接级别。例如你目前有一个逻辑程序需要一个&o静态&r数据。如果在将来的某个时候, 你想改变这个数据, 你得重写你的程序, 除非那个数据是&l动态&r的, 在这种情况下你只需要在&l代理器&r升级这个数据。\",\n    \"info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5\": \"本教程,制作复制器和代理器&l变量卡&r.\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations\": \"高级运算符\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text1\": \"通过学习最近的教程你可能注意到了, &l逻辑编程器&r的功能不仅仅是相加加数字。该教程中, 我们将研究更先进的数据结构和链状运算符。\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text2\": \"让我们创建一个静态&6Integer&0&7列表&0 &61&0, &610&0 和 &6100&0, 并让它在&l显示面板&r可视化。\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text3\": \"&7物品&0也是这个mod中数据的类型之一。 让我们做一张静态&7物品&0 &l变量卡&r,它能自动检索出该物品在mod中的名称,让它在&l显示面板&r显示出来吧。\",\n    \"info_book.integrateddynamics.tutorials.advancedOperations.text4\": \"记住你只能用&l变量卡箱&r 来储存引用的变量。\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators\": \"运算符的数据\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text2\": \"第一步，打开&l逻辑编程器&r,创建一个新的静态&2运算符&0, 在列表中点击&2Operator&0打开它的界面，并输入Arithmetic addition,并写入&l变量卡&r，接下来做两张&6Integer&0的变量。找到&oApply&r运算符, 把第一个&6Integer&0应用到&2运算符&0上, 会得到一个新的带有输入数据和一个输入空位的&2运算符&0。接下来,再次使用&oApply&r运算符去&o绑定&r新创建的&2运算符&0的后一个输入空位。最后,用&l显示面板&r来可视化您动态调用的加法。\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text3\": \"接下来，运用&2运算符&0,这里还有许多其他高级的&2运算符&0运算。 我们现在将研究使用&7列表&0上的&2运算符&0,更具体的说，&oFilter&r&2运算符&0需要用到一个&7List&0和一个&oPredicate&r (只有一个输入位和一个&9Boolean&0 输出数据的&2运算符&0)。首先创建一个&7List&0 &6Integers&0 &61&0, &610&0 and &6100&0。然后, 存储&oRelational Equals&r &2运算符&0 到变量中。然后, 将该运算符应用到&6Integer&0 &610&0。 然后, 通过&2运算符&0 with 你的&7List&0和应用的&2运算符&0来使用&oFilter&r 。 这将通过在指定的运算符上应用它们来过滤掉&7List&0中返回&9False&0的所有项目，在这种情况下，这意味着所有不等于&610&0的元素将从列表中移除。将它在&l显示面板&r中可视化。\",\n    \"info_book.integrateddynamics.tutorials.theValueOfOperators.text4\": \"&2运算符&0有很多其他的操作, 例如组合&oPredicates&r，翻转和2运算符和0参数，以及&7Lists&0的更多操作。\",\n    \"info_book.integrateddynamics.tutorials.challenges\": \"挑战\",\n    \"info_book.integrateddynamics.tutorials.challenges.text1\": \"本最终教程部分包含几个非常微不足道的挑战，以表明您了解&1动态联合&0的基础知识。每个挑战中不会有提示，所以祝你好运！\",\n    \"info_book.integrateddynamics.manual\": \"菜单\",\n    \"info_book.integrateddynamics.manual.introduction\": \"介绍\",\n    \"info_book.integrateddynamics.manual.prerequisites\": \"先决条件\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees\": \"门瑞欧树\",\n    \"info_book.integrateddynamics.manual.prerequisites.menril_trees.text1\": \"你偶尔会遇到高大的蓝色树木。这样的树叫做 &l门瑞欧树&r, 并有一些非常有趣的属性。其一, 叶子会发光。这是由照明的浆果导致的，它非常的好吃！其二, 色质特别的原木包含大量的树脂，可用于制作这个MOD中的大部分内容。\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\": \"门勒格林\",\n    \"info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1\": \"&l门瑞欧树&r 很难找到。幸运的是, 本mod为此增加了一个生物群系, &l门勒格林&r ，它是门瑞欧树茁壮生长的群系. 大量的&l门瑞欧树&r在这个生物群中引发了一些事情使得该群系看起来很蓝,这是因为树木中的&l门瑞欧树脂&r被释放到了环境中。\",\n    \"info_book.integrateddynamics.manual.prerequisites.squeezer\": \"挤压机\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin\": \"洪干池\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text1\": \"干燥方块和物品可以用&l洪干池&r完成。例如，当您希望让您的&l门瑞欧树脂&r干燥成&l门瑞欧结晶&r时，这可能是有用的。\",\n    \"info_book.integrateddynamics.manual.prerequisites.drying_basin.text2\": \"无论如何，不要注入热流体！\",\n    \"info_book.integrateddynamics.manual.networks\": \"网络\",\n    \"info_book.integrateddynamics.manual.networks.text1\": \"&l动态联合&r &l网络&r是一切自动化系统的基础。 网络有相互连接的&l逻辑线缆&r构成，一些部件可以直接安置在上面。 这些电缆应使用&l扳手&r进行管理。Shift+右击一个线缆或者部件将其拆卸, 而常规右键单击将与其他电缆断开连接。\",\n    \"info_book.integrateddynamics.manual.logic\": \"逻辑\",\n    \"info_book.integrateddynamics.manual.logic.value_types\": \"数据类型\",\n    \"info_book.integrateddynamics.manual.logic.value_types.boolean.text1\": \"最简单的数据类型, 其数据可以是&9True(真)&0或者&9False(假)&0.\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text1\": \"用于表示正整数和负整数的数据类型。例如 &6-20&0, &60&0, &61000&0, &6333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text2\": \"它的取值范围在 &6–2147483648&0~&62147483647&0之间。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.integer.text3\": \"该数据类型是&lNumber(数字)&r类型的一部分。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text1\": \"实数，也可以是正数或负数。例如 &e3.1415&0, &e4815162342&0, &e3.33333333&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.double.text2\": \"该数据类型是&lNumber(数字)&r类型的一部分。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text1\": \"和&6Integers(整型)&0类似,但它拥有更大的取值范围： &e-9223372036854775808&0~&e9223372036854775807&0之间\",\n    \"info_book.integrateddynamics.manual.logic.value_types.long.text2\": \"该数据类型是&lNumber(数字)&r类型的一部分。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.string.text1\": \"一系列的字符，例如 &4\\\"aaa\\\"&0, &4\\\"Hi!\\\"&0, &4\\\"x!(àà)ç\\\"&0, ...\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text1\": \"包含对&2Operator(运算符)&0的引用的数据。运算符可以根据&lCurrying(柯里化)-你可能需要百度一下&r的概念部分应用于更灵活的赋值。这是&lMeta Programming(元编程)-你可能又需要百度一下&r这个概念的一部分,运算符将被其他运算符视为数据,这种方式几乎允许你创建新的运算符(和逻辑),并将它用于更复杂的操作。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text2\": \"例如你可以将加法&2Operator(运算符)&0存入变量卡中。通过在Operator(运算符)界面入运算符名称并从自动匹配的下拉列表中选择它并单击来执行此操作。 然后，您可以将其部分应用于&6Integer&0&l变量卡&r中,这将形成具有一个输入类型和一个输出类型的新的虚拟运算符，其内部包含对应用的&6Integer(整型)&0的引用。 应用另一个&6Integer(整型)&0将导致&6Integer&0。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.operator.text3\": \"&2Operator(运算符)&0的其他操作存在于&l逻辑编程器&r中以进行更高级的评估, 例如将列表减少为单个数据，或者基于&lPredicate(谓词)&r过滤列表。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text1\": \"在&lMinecraft&r中,某些东西会使用&3NBT&0这种数据结构。&3NBT&0是一种特殊类型的对象,它可以容纳不同类型的多个值。例如,&8箱子&0中存储的所有道具,&8镐子&0的附魔信息等等这些都是NBT的实际体现。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text2\": \"变量可以保存这些&3NBT&0标签的引用,并且还有一些相应的运算符来读取或扩展这些标签。\",\n    \"info_book.integrateddynamics.manual.logic.value_types.nbt.text3\": \"&3NBT&0标签通常在该Mod中作为低级别的备用机制,以防止您无法通过专用运算符来读取某些信息。(都拿到&3NBT&0了还有拿不到的数据吗?)\",\n    \"info_book.integrateddynamics.manual.logic.value_types.block.text1\": \"对&lMinecraft&r方块的引用，例如 &8泥土&0, &8圆石&0, &8红色染色黏土&0, &8箱子&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.item.text1\": \"对&lMinecraft&r物品的引用，例如 &8苹果&0, &8红石&0, &8钻石镐&0, &8附魔剑&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.entity.text1\": \"对&lMinecraft&r实体的引用，例如 &8僵尸&0, &8牛&0, &8玩家abc&0 ....\",\n    \"info_book.integrateddynamics.manual.logic.value_types.fluid.text1\": \"对&lMinecraft&r流体的引用，例如 &8岩浆@1000mB&0, &8水@3mB&0, &8门瑞欧树脂@3030mB&0, ....\",\n    \"info_book.integrateddynamics.manual.logic.variables\": \"变量\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card\": \"变量卡\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text1\": \"&l变量卡&r是这个MOD中逻辑传输的主要模式。变量包含对具有某个&l数据类型&r的某个&l数据&r的引用。\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_card.text4\": \"每个&l变量卡&r都有一个独有的ID，用于在内部网络中逻辑评测。 当网络出现错误时，这些ID将显示给玩家以帮助调试。 为了增强其可用性，可以使用&l注释器&r来给&l变量卡&r附上标签，这样可以让玩家看到标签而不是ID。\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store\": \"变量卡箱\",\n    \"info_book.integrateddynamics.manual.logic.variables.variable_store.text1\": \"一些动态变量根据其他变量进行评测，其中可以访问这些引用的变量必须可行。表示添加两个其他变量的动态变量必须能够在评测时访问这两个其他变量。访问这些其他变量必须在与正在评测动态变量的网络相同的网络中进行，这可以通过将其他变量卡放入&l变量卡箱&r来完成。网络中可存在多个变量卡箱。 这些其他变量也可以引用某些变量，这些变量也应该存在于同一个网络中。\",\n    \"info_book.integrateddynamics.manual.parts\": \"部件\",\n    \"info_book.integrateddynamics.manual.parts.introduction\": \"介绍\",\n    \"info_book.integrateddynamics.manual.parts.reader\": \"读取器\",\n    \"info_book.integrateddynamics.manual.parts.reader.introduction\": \"介绍\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio\": \"音频\",\n    \"info_book.integrateddynamics.manual.parts.reader.audio.text1\": \"有关在环境中播放的音乐和音频的信息。\",\n    \"info_book.integrateddynamics.manual.parts.reader.block\": \"方块\",\n    \"info_book.integrateddynamics.manual.parts.reader.block.text1\": \"关于部件正设置面临的阻塞的信息。\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity\": \"实体\",\n    \"info_book.integrateddynamics.manual.parts.reader.entity.text1\": \"有关该部件所在正方形内的实体或实体的信息。\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\": \"额外维度\",\n    \"info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1\": \"有关服务器的信息，如在线玩家列表。\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid\": \"流体\",\n    \"info_book.integrateddynamics.manual.parts.reader.fluid.text1\": \"从这个部件设置面临的流体容器读取数值。\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory\": \"物品栏\",\n    \"info_book.integrateddynamics.manual.parts.reader.inventory.text1\": \"从设置面向容器中读取信息，如插槽和项目数量。 这不会转移项目本身。\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine\": \"机器\",\n    \"info_book.integrateddynamics.manual.parts.reader.machine.text1\": \"该部件可读取设置面对的机器，例如他们正在做的工作。\",\n    \"info_book.integrateddynamics.manual.parts.reader.network\": \"网络\",\n    \"info_book.integrateddynamics.manual.parts.reader.network.text1\": \"阅读有关本部件所设置面临的动态联合网络的信息。 这不一定是与本部件网络相同的网络。\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone\": \"红石\",\n    \"info_book.integrateddynamics.manual.parts.reader.redstone.text1\": \"从这个部件设置面临读取红石信息。\",\n    \"info_book.integrateddynamics.manual.parts.reader.world\": \"世界\",\n    \"info_book.integrateddynamics.manual.parts.reader.world.text1\": \"关于这个部分的世界的信息，比如当前的天气和时间。\",\n    \"info_book.integrateddynamics.manual.parts.writer\": \"输出器\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio\": \"音频\",\n    \"info_book.integrateddynamics.manual.parts.writer.audio.text1\": \"播放音效和音乐。\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect\": \"效果\",\n    \"info_book.integrateddynamics.manual.parts.writer.effect.text1\": \"基于某些数据和设置发射粒子。\",\n    \"info_book.integrateddynamics.manual.parts.writer.entity.text1\": \"配置与控制实体.\",\n    \"info_book.integrateddynamics.manual.parts.writer.machine.text1\": \"配置与控制机器.\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory\": \"物品栏\",\n    \"info_book.integrateddynamics.manual.parts.writer.inventory.text1\": \"执行与库存和管道相关的操作。 这不会转移项目本身。\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone\": \"红石\",\n    \"info_book.integrateddynamics.manual.parts.writer.redstone.text1\": \"输出一个确定等级的红石信号。\",\n    \"info_book.integrateddynamics.manual.parts.other\": \"其它\",\n    \"info_book.integrateddynamics.manual.parts.other.light\": \"灯\",\n    \"info_book.integrateddynamics.manual.parts.other.light.text1\": \"一个简单的灯，可以是常开，也可以使用&1Boolean&0或&6Integer&0&l变量卡&r控制。\",\n    \"info_book.integrateddynamics.manual.parts.other.display\": \"显示\",\n    \"info_book.integrateddynamics.manual.parts.other.display.text1\": \"一个有用的监控或调试工具，只显示一个&l变量卡&r的数据。 通过&l扳手&r右键单击可以旋转显示和r。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\": \"单向连接器\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1\": \"有些时候,您的逻辑网络将会需要跨越很长的距离。虽然常规线缆可以解决问题,但是长距离放置线缆并不是非常方便。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2\": \"&l单向连接器&r可以使您用无线的方式长距离连接网络。通过在一个网络上放置第一个连接器,并在另一个网络上放置另一个连接器来实现。这两个连接器必须处于同一直线上(既两个连接器的坐标值X,Y,Z之中的2个值相等),且距离小于或等于512的方块。当链接有效时,连接器将显示为有效状态。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\": \"全向连接器\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1\": \"虽然&l单向连接器&r对于单个方向上的长距离链接很有用,但是在某些情况下还是有所限制。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2\": \"例如,您可能想使用单个连接器将多个网络链接起来,或者与其他维度的网络通信。对于这些情况,您可以使用&l全向连接器&r。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3\": \"这些连接器在首次制作时将产生一个唯一的分组ID,标识可连接网络的分组。每个连接器组在放置在世界中的时候会散发出独有的彩色粒子,分组ID可以在道具信息中看到,或者右键单击放置下来的连接器。通过使用分组内的现有的连接器制作新的连接器,可以将其他连接器添加到组中。\",\n    \"info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4\": \"也可以通过两种方式更改现有连接器组. 在制作工作台中单独制作会将其分配给没有其他成员的新组, 将二个放在一起合成时第一个的分组将复制给第二个.\",\n    \"info_book.integrateddynamics.manual.machines\": \"机器\",\n    \"info_book.integrateddynamics.manual.machines.materializer\": \"复制机\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text1\": \"复制动态&l变量卡&r当前时刻的静态数据。必须插入动态&l变量卡&r，并且必须插入一个新的空&l变量卡&r，这将使其具有动态&l变量卡&r的当前值作为静态值。\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text2\": \"在您想要记录某个动态和变量卡数据的情况下，或者当您不再需要动态评测&l变量卡&r的情况下，这将非常有用。\",\n    \"info_book.integrateddynamics.manual.machines.materializer.text3\": \"&l变量卡&r可在&l复制器&r旁用管道输入和输出。\",\n    \"info_book.integrateddynamics.manual.machines.proxy\": \"代理器\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text1\": \"&l代理器&r可以用来动态地引用某个&l变量卡&r。它允许您插入&l变量卡&r，并写入一个新的&l变量卡&r，它将动态地获取代理中存在的&l变量卡&r的值。\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text2\": \"这在您具有&l变量卡&r链的情况下非常有用，但您仍然可以手动更改一个数据。然后，您可以代理该变量，这将允许它随时与另一个变量交换。\",\n    \"info_book.integrateddynamics.manual.machines.proxy.text3\": \"正如&l复制器&r，&l变量卡&r可以管道输入和输出。\",\n    \"info_book.integrateddynamics.manual.machines.delay.text1\": \"在某些情况下，您可能需要维护某种变量数据的历史。例如，当您想要获取上一个值的平均值或差值时。\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery\": \"能量电池\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text1\": \"&l动态联合&r网络可以配置为消耗能量。为此，您可以将&l能量电池&r连接到网络，为其添加能量存储容量。\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text3\": \"应用红石信号将使能量的自动出口模式到所有设置面。\",\n    \"info_book.integrateddynamics.manual.machines.energy_battery.text4\": \"当这个电池在你的包裹中时, &l自动输出&r模式可以使用Shift+右键激活. 激活后,这个电池会尝试输出能量到你双手中的物品.\",\n    \"info_book.integrateddynamics.manual.machines.generator\": \"发电机\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_squeezer\": \"电动挤压机\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\": \"电动烘干池\",\n    \"info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1\": \"使用&l烘干池&r进行烘干会需要一些时间.如果你想要烘干变得更快,或者你想能烘干更多的东西,你应该使用&l电动烘干池&r,它需要消耗能量来运行.\",\n    \"info_book.integrateddynamics.manual.logic_programming\": \"逻辑编程\",\n    \"info_book.integrateddynamics.manual.logic_programming.text1\": \"创建动态变量卡的另一种方法是通过在一个或多个变量卡上应用&l运算符&r来生成新的&l变量卡&r。一个&l运算符&r的一个例子是添加了两个&lnumber&r，形成一个新的&lnumber&r。\",\n    \"info_book.integrateddynamics.manual.logic_programming.text3\": \"&l逻辑编程器&r包含大量的&l运算符&r的集合。它还允许您为所有&l数据类型&r创建静态和变量卡&r。 选择&l运算符&r后，应在预期的输入插槽中插入有效&l变量卡&r。 最后，应在输出插槽中插入另一个&l变量卡&r。如果&l运算符&r有效，则会将新的动态变量写入卡中。 请记住，引用的&l变量卡&r必须存在于&l运算符&r将被评测的同一网络中的&l变量卡箱&r中。\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/lang/zh_tw.json",
    "content": "{}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/any/network/value.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/any/network/value\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/block/block.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/block/block.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/block/block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/applicable.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/applicable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/canextract.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/canextract\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/caninsert.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/caninsert\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/isempty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/isempty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/isfull.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/isfull\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/isnonempty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/isnonempty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/isprovider.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/isprovider\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/energy/isreceiver.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/energy/isreceiver\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/fluid/applicable.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/fluid/applicable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/fluid/empty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/fluid/empty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/fluid/full.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/fluid/full\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/fluid/nonempty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/fluid/nonempty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/inventory/applicable.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/inventory/applicable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/inventory/empty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/inventory/empty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/inventory/full.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/inventory/full\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/inventory/nonempty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/inventory/nonempty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/machine/canwork.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/machine/canwork\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/machine/haswork.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/machine/haswork\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/machine/isworker.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/machine/isworker\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/machine/isworking.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/machine/isworking\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/network/applicable.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/network/applicable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/recipehandler/applicable.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/recipehandler/applicable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/redstone/clock.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/redstone/clock\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/redstone/high.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/redstone/high\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/redstone/low.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/redstone/low\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/redstone/nonlow.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/redstone/nonlow\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/temperature/istemperature.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/temperature/istemperature\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/animal.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/animal\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/entity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/entity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/isday.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/isday\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/isnight.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/isnight\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/item.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/item\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/mob.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/mob\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/player.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/player\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/weather/clear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/weather/clear\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/weather/raining.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/weather/raining\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/boolean/world/weather/thunder.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/boolean/world/weather/thunder\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/energy/fillratio.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/energy/fillratio\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/extradimensional/tps.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/extradimensional/tps\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/fluid/fillratio.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/fluid/fillratio\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/inventory/fillratio.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/inventory/fillratio\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/temperature/defaulttemperature.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/temperature/defaulttemperature\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/temperature/maxtemperature.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/temperature/maxtemperature\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/temperature/mintemperature.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/temperature/mintemperature\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/temperature/temperature.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/temperature/temperature\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/double/world/tps.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/double/world/tps\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/entity/entity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/entity/entity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/fluidstack/block.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/fluidstack/block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/fluidstack/fluid.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/fluidstack/fluid\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/banjo.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/banjo\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/basedrum.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/basedrum\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/bass.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/bass\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/bell.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/bell\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/bit.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/bit\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/chime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/chime\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/cow_bell.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/cow_bell\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/creeper.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/custom_head.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/didgeridoo.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/didgeridoo\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/dragon.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/flute.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/flute\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/guitar.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/guitar\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/harp.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/harp\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/hat.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/hat\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/iron_xylophone.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/iron_xylophone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/piglin.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/pling.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/pling\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/skeleton.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/snare.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/snare\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/wither_skeleton.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/xylophone.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/audio/instrument/xylophone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/audio/instrument/zombie.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/block/light.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/block/light\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/block/posx.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/block/posx\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/block/posy.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/block/posy\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/block/posz.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/block/posz\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/entity/itemframerotation.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/entity/itemframerotation\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/extradimensional/playercount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/extradimensional/playercount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/extradimensional/random.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/extradimensional/random\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/extradimensional/ticktime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/extradimensional/ticktime\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/fluid/tanks.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/fluid/tanks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/inventory/count.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/inventory/count\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/inventory/slots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/inventory/slots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/inventory/slotsfilled.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/inventory/slotsfilled\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/network/elementcount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/network/elementcount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/network/energy/batterycount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/network/energy/batterycount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/network/energy/consumptionrate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/network/energy/consumptionrate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/redstone/comparator.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/redstone/comparator\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/redstone/value.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/redstone/value\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/world/daytime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/world/daytime\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/world/entity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/world/entity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/world/lightlevel.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/world/lightlevel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/world/playercount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/world/playercount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/world/raincountdown.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/world/raincountdown\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/integer/world/ticktime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/integer/world/ticktime\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/itemstack/entity/itemframecontents.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/itemstack/entity/itemframecontents\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/itemstack/inventory.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/inventory/itemstack\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/entity/entities.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/entity/entities\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/entity/players.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/entity/players\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/extradimensional/players.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/extradimensional/players\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/fluid/capacities.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/fluid/capacities\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/fluid/fluidstacks.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/fluid/fluidstacks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/inventory/itemstacks.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/inventory/itemstacks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/recipehandler/recipes.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/recipehandler/recipes\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/list/world/players.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/list/world/players\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/energy/amount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/energy/amount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/energy/capacity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/energy/capacity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/fluid/amount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/fluid/amount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/fluid/capacity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/fluid/capacity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/fluid/totalamount.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/fluid/totalamount\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/fluid/totalcapacity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/fluid/totalcapacity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/network/energy/max.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/network/energy/max\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/network/energy/stored.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/network/energy/stored\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/world/time.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/world/time\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/long/world/totaltime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/long/world/totaltime\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/nbt/block/tile.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/nbt/block/tile\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/network/variablebyid.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/network/variablebyid\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/recipehandler/recipebyinput.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeinputsingle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/recipehandler/recipebyoutput.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeoutputsingle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/recipehandler/recipeinputsbyoutput.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeoutput\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/recipehandler/recipeoutputbyinput.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeinput\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/recipehandler/recipesbyinput.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeinputlist\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/operator/recipehandler/recipesbyoutput.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeoutputlist\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/string/block/biome.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/string/block/biome\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/string/block/dimension.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/string/block/dimension\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/read/string/world/worldname.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/read/string/world/worldname\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/boolean/redstone/pulse.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/boolean/redstone/pulse\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/boolean/redstone.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/boolean/redstone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_ambient_entity_effect.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_angry_villager.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_ash.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_barrier.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_bubble.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_bubble_column_up.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_bubble_pop.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_campfire_cosy_smoke.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_campfire_signal_smoke.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_cloud.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_composter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_crimson_spore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_crit.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_current_down.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_damage_indicator.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dolphin.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dragon_breath.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dripping_dripstone_lava.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dripping_dripstone_water.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dripping_honey.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dripping_lava.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dripping_obsidian_tear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dripping_water.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_dust_plume.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_effect.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_egg_crack.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_elder_guardian.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_electric_spark.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_enchant.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_enchanted_hit.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_end_rod.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_entity_effect.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_explosion.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_explosion_emitter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_dripstone_lava.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_dripstone_water.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_honey.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_lava.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_nectar.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_obsidian_tear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_spore_blossom.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_falling_water.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_firefly.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_firework.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_fishing.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_flame.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_flash.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_glow.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_glow_squid_ink.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_happy_villager.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_heart.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_instant_effect.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_item_slime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_item_snowball.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_landing_honey.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_landing_lava.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_landing_obsidian_tear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_large_smoke.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_lava.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_mycelium.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_nautilus.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_note.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_ominous_spawning.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_pause_mob_growth.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_poof.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_portal.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_raid_omen.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_rain.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_reset_mob_growth.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_reverse_portal.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_scrape.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_sculk_charge_pop.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_sculk_soul.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_small_flame.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_smoke.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_sneeze.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_snowflake.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_sonic_boom.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_soul.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_soul_fire_flame.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_spit.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_splash.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_spore_blossom_air.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_squid_ink.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_sweep_attack.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_totem_of_undying.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_trial_omen.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_trial_spawner_detection.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_trial_spawner_detection_ominous.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_underwater.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_vault_connection.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_warped_spore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_wax_off.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_wax_on.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_white_ash.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/double/effect/particle/minecraft_witch.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/double/effect/particle\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/banjo.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/banjo\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/basedrum.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/basedrum\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/bass.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/bass\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/bell.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/bell\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/bit.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/bit\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/chime.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/chime\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/cow_bell.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/cow_bell\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/creeper.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/custom_head.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/didgeridoo.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/didgeridoo\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/dragon.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/flute.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/flute\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/guitar.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/guitar\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/harp.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/harp\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/hat.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/hat\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/iron_xylophone.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/iron_xylophone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/piglin.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/pling.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/pling\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/skeleton.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/snare.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/snare\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/wither_skeleton.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/xylophone.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/audio/instrument/xylophone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/audio/instrument/zombie.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"minecraft:item/creeper_banner_pattern\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/redstone/pulse.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/redstone/pulse\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/integer/redstone.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/integer/redstone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/string/audio/sound.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/string/audio/sound\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/aspect/write/string/audio/text.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:aspect/write/string/audio/text\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/cable.json",
    "content": "{\n  \"parent\": \"block/block\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/coal_generator.json",
    "content": "{\n  \"parent\": \"block/orientable\",\n  \"textures\": {\n    \"top\": \"integrateddynamics:block/variablestore_top\",\n    \"front\": \"integrateddynamics:block/coal_generator_front_off\",\n    \"side\": \"integrateddynamics:block/coal_generator_side\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/coal_generator_on.json",
    "content": "{\n  \"parent\": \"block/orientable\",\n  \"textures\": {\n    \"top\": \"integrateddynamics:block/variablestore_top\",\n    \"front\": \"integrateddynamics:block/coal_generator_front_on\",\n    \"side\": \"integrateddynamics:block/coal_generator_side\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_block_slab.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_block_slab_top.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab_top\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_block_stairs.json",
    "content": "{\n  \"parent\": \"block/stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_block_stairs_inner.json",
    "content": "{\n  \"parent\": \"block/inner_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_block_stairs_outer.json",
    "content": "{\n  \"parent\": \"block/outer_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_block\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_brick.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_brick_slab.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_brick_slab_top.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab_top\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_brick_stairs.json",
    "content": "{\n  \"parent\": \"block/stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_brick_stairs_inner.json",
    "content": "{\n  \"parent\": \"block/inner_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_chorus_brick_stairs_outer.json",
    "content": "{\n  \"parent\": \"block/outer_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_chorus_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_chorus_brick\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_block_slab.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_block_slab_top.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab_top\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_block_stairs.json",
    "content": "{\n  \"parent\": \"block/stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_block_stairs_inner.json",
    "content": "{\n  \"parent\": \"block/inner_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_block_stairs_outer.json",
    "content": "{\n  \"parent\": \"block/outer_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_block\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_brick.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_brick_slab.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_brick_slab_top.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab_top\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_brick_stairs.json",
    "content": "{\n  \"parent\": \"block/stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_brick_stairs_inner.json",
    "content": "{\n  \"parent\": \"block/inner_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/crystalized_menril_brick_stairs_outer.json",
    "content": "{\n  \"parent\": \"block/outer_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"top\": \"integrateddynamics:block/crystalized_menril_brick\",\n    \"side\": \"integrateddynamics:block/crystalized_menril_brick\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/delay.json",
    "content": "{\n    \"parent\": \"block/cube\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/delay_top\",\n        \"down\": \"integrateddynamics:block/delay_top\",\n        \"up\": \"integrateddynamics:block/delay_top\",\n        \"north\": \"integrateddynamics:block/delay_front\",\n        \"east\": \"integrateddynamics:block/delay_side\",\n        \"south\": \"integrateddynamics:block/delay_front\",\n        \"west\": \"integrateddynamics:block/delay_side\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/drying_basin.json",
    "content": "{\n    \"parent\": \"block/block\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/drying_basin\"\n    },\n    \"elements\": [\n    {\n        \"__comment\": \"Lower_rim1\",\n        \"from\": [ 0.5625, 10, 0.5 ],\n        \"to\": [ 1.5625, 12, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 8, 8.5, 8.5, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 8.5, 8.5, 9, 16 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 7, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 15, 0.5, 16 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Panel2\",\n        \"from\": [ 1, 3, 1 ],\n        \"to\": [ 2, 15, 15 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 7.5, 0, 8, 7 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 7.5, 0, 8, 7 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0, 0, 0.5, 6 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 6.5, 0, 7, 6 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 0, 7, 6 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 7, 0, 0, 6 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Panel3\",\n        \"from\": [ 14, 3, 1 ],\n        \"to\": [ 15, 15, 15 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 7.5, 0, 8, 7 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 7.5, 7, 8, 0 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0, 0, 0.5, 6 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 6.5, 0, 7, 6 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 7, 0, 0, 6 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 0, 7, 6 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Panel4\",\n        \"from\": [ 2, 3, 1 ],\n        \"to\": [ 14, 15, 2 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 6.5, 0, 0.5, 0.5 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 6.5, 0, 0.5, 0.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0.5, 0, 6.5, 6 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 6.5, 0, 0.5, 6 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Panel1\",\n        \"from\": [ 2, 3, 14 ],\n        \"to\": [ 14, 15, 15 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0.5, 0, 6.5, 0.5 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 0.5, 0, 6.5, 0.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 6.5, 0, 0.5, 6 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0.5, 0, 6.5, 6 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_foot1\",\n        \"from\": [ 1, 2, 11 ],\n        \"to\": [ 5, 3, 15 ],\n        \"faces\": {\n            \"up\":    { \"uv\": [ 3, 0.5, 5, 2.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Floor\",\n        \"from\": [ 2, 3, 2 ],\n        \"to\": [ 14, 4, 14 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0.5, 0, 6.5, 6 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 0.5, 0, 6.5, 6 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_foot2\",\n        \"from\": [ 11, 2, 11 ],\n        \"to\": [ 15, 3, 15 ],\n        \"faces\": {\n            \"up\":    { \"uv\": [ 5, 0.5, 3, 2.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_foot3\",\n        \"from\": [ 1, 2, 1 ],\n        \"to\": [ 5, 3, 5 ],\n        \"faces\": {\n            \"up\":    { \"uv\": [ 3, 0.5, 5, 2.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_foot4\",\n        \"from\": [ 11, 2, 1 ],\n        \"to\": [ 15, 3, 5 ],\n        \"faces\": {\n            \"up\":    { \"uv\": [ 3, 0.5, 5, 2.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 8.5, 0, 10.5, 0.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 10.5, 0, 8.5, 0.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_rim2\",\n        \"from\": [ 1.5, 10, 14.5 ],\n        \"to\": [ 14.5, 12, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0.5, 15.5, 7, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 0.5, 15, 7, 15.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 15, 0.5, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_rim3\",\n        \"from\": [ 14.5, 10, 0.5 ],\n        \"to\": [ 15.5, 12, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 8, 8.5, 8.5, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 8.5, 8.5, 9, 16 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 7, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 15, 0.5, 16 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_rim4\",\n        \"from\": [ 1.5, 10, 0.5 ],\n        \"to\": [ 14.5, 12, 1.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0.5, 15.5, 7, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 0.5, 15, 7, 15.5 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 15, 0.5, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_foot1\",\n        \"from\": [ 0.5, 0, 10.5 ],\n        \"to\": [ 5.5, 2, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0, 10.5, 2.5, 13 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n            \"up\":    { \"uv\": [ 3, 10.5, 5.5, 13 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_foot2\",\n        \"from\": [ 10.5, 0, 10.5 ],\n        \"to\": [ 15.5, 2, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0, 10.5, 2.5, 13 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n            \"up\":    { \"uv\": [ 3, 10.5, 5.5, 13 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_foot3\",\n        \"from\": [ 10.5, 0, 0.5 ],\n        \"to\": [ 15.5, 2, 5.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0, 10.5, 2.5, 13 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n            \"up\":    { \"uv\": [ 3, 10.5, 5.5, 13 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Lower_foot4\",\n        \"from\": [ 0.5, 0, 0.5 ],\n        \"to\": [ 5.5, 2, 5.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0, 10.5, 2.5, 13 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n            \"up\":    { \"uv\": [ 3, 10.5, 5.5, 13 ], \"texture\": \"#texture\" },\n            \"north\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 13.5, 2.5, 14.5 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_rim1\",\n        \"from\": [ 1.5, 14, 14.5 ],\n        \"to\": [ 14.5, 16, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0.5, 15.5, 7, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 0.5, 15, 7, 15.5 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n            \"north\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_rim2\",\n        \"from\": [ 0.5, 14, 0.5 ],\n        \"to\": [ 1.5, 16, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 8, 8.5, 8.5, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 8.5, 8.5, 9, 16 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n            \"north\": { \"uv\": [ 7, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 15, 0.5, 16 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_rim3\",\n        \"from\": [ 14.5, 14, 0.5 ],\n        \"to\": [ 15.5, 16, 15.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 8, 8.5, 8.5, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 8.5, 8.5, 9, 16 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n            \"north\": { \"uv\": [ 7, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0, 15, 0.5, 16 ], \"texture\": \"#texture\" },\n            \"west\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" },\n            \"east\":  { \"uv\": [ 0, 15, 7.5, 16 ], \"texture\": \"#texture\" }\n        }\n    },\n    {\n        \"__comment\": \"Top_rim4\",\n        \"from\": [ 1.5, 14, 0.5 ],\n        \"to\": [ 14.5, 16, 1.5 ],\n        \"faces\": {\n            \"down\":  { \"uv\": [ 0.5, 15.5, 7, 16 ], \"texture\": \"#texture\" },\n            \"up\":    { \"uv\": [ 0.5, 15, 7, 15.5 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n            \"north\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" },\n            \"south\": { \"uv\": [ 0.5, 15, 7, 16 ], \"texture\": \"#texture\" }\n        }\n    }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/energy_battery.json",
    "content": "{\n    \"parent\": \"block/orientable\",\n    \"textures\": {\n        \"top\": \"integrateddynamics:block/energy_battery_up\",\n        \"front\": \"integrateddynamics:block/energy_battery_side\",\n        \"side\": \"integrateddynamics:block/energy_battery_side\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/energy_battery_creative.json",
    "content": "{\n    \"parent\": \"block/orientable\",\n    \"textures\": {\n        \"top\": \"integrateddynamics:block/energy_battery_up\",\n        \"front\": \"integrateddynamics:block/energy_battery_side_creative\",\n        \"side\": \"integrateddynamics:block/energy_battery_side_creative\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/energy_battery_full.json",
    "content": "{\n    \"parent\": \"block/orientable\",\n    \"textures\": {\n        \"top\": \"integrateddynamics:block/energy_battery_up\",\n        \"front\": \"integrateddynamics:block/energy_battery_side_full\",\n        \"side\": \"integrateddynamics:block/energy_battery_side_full\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/invisible_light.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"cyclopscore:block/empty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/liquid_chorus.json",
    "content": "{\n  \"textures\": {\n    \"particle\": \"integrateddynamics:block/liquid_chorus_still\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/logic_programmer.json",
    "content": "{\n    \"parent\": \"block/cube\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/logic_programmer_top\",\n        \"down\": \"integrateddynamics:block/variablestore_top\",\n        \"up\": \"integrateddynamics:block/logic_programmer_top\",\n        \"north\": \"integrateddynamics:block/logic_programmer_side\",\n        \"east\": \"integrateddynamics:block/logic_programmer_side\",\n        \"south\": \"integrateddynamics:block/logic_programmer_side\",\n        \"west\": \"integrateddynamics:block/logic_programmer_side\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/materializer.json",
    "content": "{\n    \"parent\": \"block/cube\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/proxy_top\",\n        \"down\": \"integrateddynamics:block/proxy_top\",\n        \"up\": \"integrateddynamics:block/proxy_top\",\n        \"north\": \"integrateddynamics:block/materializer_front\",\n        \"east\": \"integrateddynamics:block/proxy_side\",\n        \"south\": \"integrateddynamics:block/proxy_back\",\n        \"west\": \"integrateddynamics:block/proxy_side\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/mechanical_drying_basin.json",
    "content": "{\n\t\"parent\": \"block/block\",\n\t\"ambientocclusion\": false,\n\t\"textures\": {\n\t\t\"texture\": \"integrateddynamics:block/mechanical_drying_basin_off\",\n\t\t\"particle\": \"integrateddynamics:block/menril_log\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t1,\n\t\t\t\t3,\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t2,\n\t\t\t\t15,\n\t\t\t\t15\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t9,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t13.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t13.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t14,\n\t\t\t\t3,\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t15,\n\t\t\t\t15,\n\t\t\t\t15\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t9,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t6.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t13.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t14,\n\t\t\t\t15,\n\t\t\t\t2\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t9.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t0.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t0.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\t14\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t14,\n\t\t\t\t15,\n\t\t\t\t15\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t9.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t6\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t1,\n\t\t\t\t2,\n\t\t\t\t11\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t5,\n\t\t\t\t3,\n\t\t\t\t15\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t1.5,\n\t\t\t\t15,\n\t\t\t\t1.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t14.5,\n\t\t\t\t16,\n\t\t\t\t14.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t9.5,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t10.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t9.5,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t10.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t9,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t10.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t10.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\t2\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t14,\n\t\t\t\t4,\n\t\t\t\t14\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t9.5,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t9.5,\n\t\t\t\t\t\t10,\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t11,\n\t\t\t\t2,\n\t\t\t\t11\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t15,\n\t\t\t\t3,\n\t\t\t\t15\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t1,\n\t\t\t\t2,\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t5,\n\t\t\t\t3,\n\t\t\t\t5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t11,\n\t\t\t\t2,\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t15,\n\t\t\t\t3,\n\t\t\t\t5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t2,\n\t\t\t\t\t\t6.5,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t7\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t0.5,\n\t\t\t\t0,\n\t\t\t\t10.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t5.5,\n\t\t\t\t2,\n\t\t\t\t15.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t5.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"down\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t10.5,\n\t\t\t\t0,\n\t\t\t\t10.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t15.5,\n\t\t\t\t2,\n\t\t\t\t15.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t5.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"down\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t10.5,\n\t\t\t\t0,\n\t\t\t\t0.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t15.5,\n\t\t\t\t2,\n\t\t\t\t5.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t5.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"down\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t0.5,\n\t\t\t\t0,\n\t\t\t\t0.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t5.5,\n\t\t\t\t2,\n\t\t\t\t5.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t14.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t5.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t10.5,\n\t\t\t\t\t\t2.5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"down\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t1.5,\n\t\t\t\t12,\n\t\t\t\t14.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t14.5,\n\t\t\t\t14,\n\t\t\t\t15.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t15.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"up\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t0.5,\n\t\t\t\t12,\n\t\t\t\t0.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t1.5,\n\t\t\t\t14,\n\t\t\t\t15.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t9,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"up\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t14.5,\n\t\t\t\t12,\n\t\t\t\t0.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t15.5,\n\t\t\t\t14,\n\t\t\t\t15.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t9,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"up\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t8.5,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t1.5,\n\t\t\t\t12,\n\t\t\t\t0.5\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t14.5,\n\t\t\t\t14,\n\t\t\t\t1.5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t15.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\",\n\t\t\t\t\t\"cullface\": \"up\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0.5,\n\t\t\t\t\t\t15.5,\n\t\t\t\t\t\t7,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cube\",\n\t\t\t\"from\": [\n\t\t\t\t0,\n\t\t\t\t15,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t16,\n\t\t\t\t16,\n\t\t\t\t16\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t0.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t8\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t7.5,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t8\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t0.5\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t8\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t8,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t8\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#texture\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/mechanical_drying_basin_on.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/mechanical_drying_basin\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/mechanical_drying_basin_on\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/mechanical_squeezer.json",
    "content": "{\n\t\"parent\": \"block/block\",\n\t\"ambientocclusion\": false,\n\t\"textures\": {\n\t\t\"side\": \"integrateddynamics:block/mechanical_squeezer_x_off\",\n\t\t\"y\": \"integrateddynamics:block/mechanical_squeezer_y\",\n\t\t\"particle\": \"integrateddynamics:block/mechanical_squeezer_x_off\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"name\": \"top\",\n\t\t\t\"from\": [\n\t\t\t\t0,\n\t\t\t\t13,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t16,\n\t\t\t\t16,\n\t\t\t\t16\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t3\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t3\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t3\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t3\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\",\n\t\t\t\t\t\"rotation\": 180\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"base\",\n\t\t\t\"from\": [\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t16,\n\t\t\t\t3,\n\t\t\t\t16\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t13,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"pillar1\",\n\t\t\t\"from\": [\n\t\t\t\t11,\n\t\t\t\t3,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t16,\n\t\t\t\t13,\n\t\t\t\t5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\",\n\t\t\t\t\t\"rotation\": 180\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"pillar2\",\n\t\t\t\"from\": [\n\t\t\t\t0,\n\t\t\t\t3,\n\t\t\t\t11\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t5,\n\t\t\t\t13,\n\t\t\t\t16\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\",\n\t\t\t\t\t\"rotation\": 180\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"pillar3\",\n\t\t\t\"from\": [\n\t\t\t\t11,\n\t\t\t\t3,\n\t\t\t\t11\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t16,\n\t\t\t\t13,\n\t\t\t\t16\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\",\n\t\t\t\t\t\"rotation\": 180\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"pillar4\",\n\t\t\t\"from\": [\n\t\t\t\t0,\n\t\t\t\t3,\n\t\t\t\t0\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t5,\n\t\t\t\t13,\n\t\t\t\t5\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t5,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t11,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\",\n\t\t\t\t\t\"rotation\": 180\n\t\t\t\t},\n\t\t\t\t\"up\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\t16,\n\t\t\t\t\t\t16\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#y\",\n\t\t\t\t\t\"rotation\": 270\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"center\",\n\t\t\t\"from\": [\n\t\t\t\t4,\n\t\t\t\t3,\n\t\t\t\t4\n\t\t\t],\n\t\t\t\"to\": [\n\t\t\t\t12,\n\t\t\t\t13,\n\t\t\t\t12\n\t\t\t],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t4,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t12,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"east\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t4,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t12,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"south\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t4,\n\t\t\t\t\t\t3,\n\t\t\t\t\t\t12,\n\t\t\t\t\t\t13\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\"\n\t\t\t\t},\n\t\t\t\t\"west\": {\n\t\t\t\t\t\"uv\": [\n\t\t\t\t\t\t12,\n\t\t\t\t\t\t13,\n\t\t\t\t\t\t4,\n\t\t\t\t\t\t3\n\t\t\t\t\t],\n\t\t\t\t\t\"texture\": \"#side\",\n\t\t\t\t\t\"rotation\": 180\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"cube\"\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/mechanical_squeezer_on.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/mechanical_squeezer\",\n  \"textures\": {\n    \"side\": \"integrateddynamics:block/mechanical_squeezer_x_on\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_door_bottom.json",
    "content": "{\n  \"parent\": \"minecraft:block/door_bottom_left\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/door_menril_lower\",\n    \"top\": \"integrateddynamics:block/door_menril_upper\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_door_bottom_rh.json",
    "content": "{\n  \"parent\": \"minecraft:block/door_bottom_right\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/door_menril_lower\",\n    \"top\": \"integrateddynamics:block/door_menril_upper\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_door_top.json",
    "content": "{\n  \"parent\": \"minecraft:block/door_top_left\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/door_menril_lower\",\n    \"top\": \"integrateddynamics:block/door_menril_upper\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_door_top_rh.json",
    "content": "{\n  \"parent\": \"minecraft:block/door_top_right\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/door_menril_lower\",\n    \"top\": \"integrateddynamics:block/door_menril_upper\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_gate.json",
    "content": "{\n  \"parent\": \"minecraft:block/template_fence_gate\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_gate_open.json",
    "content": "{\n  \"parent\": \"minecraft:block/template_fence_gate_open\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_gate_wall.json",
    "content": "{\n  \"parent\": \"minecraft:block/template_fence_gate_wall\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_gate_wall_open.json",
    "content": "{\n  \"parent\": \"minecraft:block/template_fence_gate_wall_open\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_inventory.json",
    "content": "{\n  \"parent\": \"minecraft:block/fence_inventory\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_post.json",
    "content": "{\n  \"parent\": \"minecraft:block/fence_post\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_fence_side.json",
    "content": "{\n  \"parent\": \"minecraft:block/fence_side\",\n  \"textures\": {\n    \"texture\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_leaves.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/menril_leaves_fancy\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_log.json",
    "content": "{\n  \"parent\": \"block/cube_column\",\n  \"textures\": {\n    \"end\" : \"integrateddynamics:block/menril_log_top\",\n    \"side\": \"integrateddynamics:block/menril_log\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_log_filled.json",
    "content": "{\n  \"parent\": \"block/cube_column\",\n  \"textures\": {\n    \"end\" : \"integrateddynamics:block/menril_log_top\",\n    \"side\": \"integrateddynamics:block/menril_log\",\n    \"north\": \"integrateddynamics:block/menril_log_filled\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_log_filled_side.json",
    "content": "{\n  \"parent\": \"block/cube\",\n  \"textures\": {\n    \"north\" : \"integrateddynamics:block/menril_log_top\",\n    \"south\" : \"integrateddynamics:block/menril_log_top\",\n    \"up\": \"integrateddynamics:block/menril_log_filled\",\n    \"down\": \"integrateddynamics:block/menril_log\",\n    \"east\": \"integrateddynamics:block/menril_log\",\n    \"west\": \"integrateddynamics:block/menril_log\",\n    \"particle\": \"integrateddynamics:block/menril_log\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_log_stripped.json",
    "content": "{\n  \"parent\": \"block/cube_column\",\n  \"textures\": {\n    \"end\" : \"integrateddynamics:block/menril_log_top_stripped\",\n    \"side\": \"integrateddynamics:block/menril_log_stripped\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_planks.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_planks_stairs.json",
    "content": "{\n  \"parent\": \"block/stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/menril_planks\",\n    \"top\": \"integrateddynamics:block/menril_planks\",\n    \"side\": \"integrateddynamics:block/menril_planks\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_planks_stairs_inner.json",
    "content": "{\n  \"parent\": \"block/inner_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/menril_planks\",\n    \"top\": \"integrateddynamics:block/menril_planks\",\n    \"side\": \"integrateddynamics:block/menril_planks\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_planks_stairs_outer.json",
    "content": "{\n  \"parent\": \"block/outer_stairs\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/menril_planks\",\n    \"top\": \"integrateddynamics:block/menril_planks\",\n    \"side\": \"integrateddynamics:block/menril_planks\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_resin.json",
    "content": "{\n  \"textures\": {\n    \"particle\": \"integrateddynamics:block/menril_resin_still\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_sapling.json",
    "content": "{\n  \"parent\": \"block/cross\",\n  \"render_type\": \"minecraft:cutout\",\n  \"textures\": {\n    \"cross\": \"integrateddynamics:block/menril_sapling\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_slab.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/menril_planks\",\n    \"top\": \"integrateddynamics:block/menril_planks\",\n    \"side\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_slab_top.json",
    "content": "{\n  \"parent\": \"minecraft:block/slab_top\",\n  \"textures\": {\n    \"bottom\": \"integrateddynamics:block/menril_planks\",\n    \"top\": \"integrateddynamics:block/menril_planks\",\n    \"side\": \"integrateddynamics:block/menril_planks\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_torch.json",
    "content": "{\n  \"parent\": \"block/template_torch\",\n  \"render_type\": \"minecraft:cutout\",\n  \"textures\": {\n    \"torch\": \"integrateddynamics:block/menril_torch\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_torch_stone.json",
    "content": "{\n  \"parent\": \"block/template_torch\",\n  \"render_type\": \"minecraft:cutout\",\n  \"textures\": {\n    \"torch\": \"integrateddynamics:block/menril_torch_stone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_torch_stone_wall.json",
    "content": "{\n  \"parent\": \"block/wall_torch\",\n  \"render_type\": \"minecraft:cutout\",\n  \"textures\": {\n    \"torch\": \"integrateddynamics:block/menril_torch_stone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_torch_wall.json",
    "content": "{\n  \"parent\": \"block/wall_torch\",\n  \"render_type\": \"minecraft:cutout\",\n  \"textures\": {\n    \"torch\": \"integrateddynamics:block/menril_torch\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_wood.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/menril_log\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/menril_wood_stripped.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/menril_log_stripped\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part.json",
    "content": "{\n    \"parent\": \"block/block\",\n    \"display\": {\n        \"thirdperson_righthand\": {\n            \"rotation\": [ 10, -45, 170 ],\n            \"translation\": [ 0, 1.5, -2.75 ],\n            \"scale\": [ 0.375, 0.375, 0.375 ]\n        },\n        \"gui\": {\n            \"rotation\": [ 30, 45, 0 ],\n            \"translation\": [ -4, 2, 0],\n            \"scale\":[ 1, 1, 1 ]\n        },\n        \"firstperson_righthand\": {\n            \"rotation\": [ 0, 45, 0 ],\n            \"translation\": [ 0, 2, 0 ],\n            \"scale\": [ 0.40, 0.40, 0.40 ]\n        },\n        \"firstperson_lefthand\": {\n            \"rotation\": [ 0, 45, 0 ],\n            \"translation\": [ 0, 2, 0 ],\n            \"scale\": [ 0.40, 0.40, 0.40 ]\n        }\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_audio_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/audio_reader_back\",\n        \"front\": \"integrateddynamics:part/audio_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_audio_writer_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/audio_reader_back\",\n        \"front\": \"integrateddynamics:part/audio_writer_front_active\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_audio_writer_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/audio_reader_back\",\n        \"front\": \"integrateddynamics:part/audio_writer_front_error\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_audio_writer_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/audio_reader_back\",\n        \"front\": \"integrateddynamics:part/audio_writer_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_block_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/block_reader_back\",\n        \"front\": \"integrateddynamics:part/block_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_connector_mono_directional.json",
    "content": "{\n\"parent\": \"integrateddynamics:block/part\",\n\"__comment\": \"Model generated using MrCrayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n\"textures\": {\n    \"front\": \"integrateddynamics:part/part_connector_mono_directional_front\",\n    \"back\": \"integrateddynamics:part/part_connector_mono_directional_back\",\n    \"particle\": \"integrateddynamics:block/cable\"\n},\n\"elements\": [ \n{\n    \"from\": [ 4, 4, 15 ],\n    \"to\": [ 12, 12, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 8, 1 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 0, 0, 8, 1 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 0, 2, 8, 10 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 0, 2, 8, 10 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 0, 0, 8, 1 ], \"rotation\": 270, \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 0, 0, 8, 1 ], \"rotation\": 270, \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 6, 6, 13 ],\n    \"to\": [ 10, 10, 15 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 4, 16, 0, 14 ], \"rotation\": 180, \"texture\": \"#back\" },\n        \"up\":    { \"uv\": [ 4, 16, 0, 14 ], \"texture\": \"#back\" },\n        \"west\":  { \"uv\": [ 0, 14, 4, 16 ], \"rotation\": 90, \"texture\": \"#back\" },\n        \"east\":  { \"uv\": [ 0, 16, 4, 14 ], \"rotation\": 90, \"texture\": \"#back\" }\n    }\n},\n{\n    \"from\": [ 5, 5, 12 ],\n    \"to\": [ 11, 11, 13 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 0, 15, 6, 16 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 0, 15, 6, 16 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 10, 10, 16, 16 ], \"texture\": \"#back\" },\n        \"south\": { \"uv\": [ 10, 10, 16, 16 ], \"texture\": \"#back\" },\n        \"west\":  { \"uv\": [ 15, 0, 16, 6 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 15, 0, 16, 6 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 6, 6, 11 ],\n    \"to\": [ 10, 10, 12 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 8, 15, 12, 16 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 8, 15, 12, 16 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 11, 11, 15, 15 ], \"texture\": \"#back\" },\n        \"west\":  { \"uv\": [ 15, 8, 16, 12 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 15, 8, 16, 12 ], \"texture\": \"#front\" }\n    }\n}\n]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_connector_mono_directional_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_connector_mono_directional\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/part_connector_mono_directional_back\",\n        \"front\": \"integrateddynamics:part/part_connector_mono_directional_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_connector_mono_directional_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_connector_mono_directional\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/part_connector_mono_directional_back\",\n        \"front\": \"integrateddynamics:part/part_connector_mono_directional_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_connector_omni_directional.json",
    "content": "{\n\"parent\": \"integrateddynamics:block/part\",\n\"__comment\": \"Model generated using MrCrayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n\"textures\": {\n    \"front\": \"integrateddynamics:part/part_connector_omni_directional_front\",\n    \"back\": \"integrateddynamics:part/part_connector_omni_directional_back\",\n    \"particle\": \"integrateddynamics:block/cable\"\n},\n\"elements\": [ \n{\n    \"from\": [ 4, 4, 15 ],\n    \"to\": [ 12, 12, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 8, 1 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 0, 0, 8, 1 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 0, 2, 8, 10 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 0, 2, 8, 10 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 0, 0, 8, 1 ], \"rotation\": 90, \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 0, 0, 8, 1 ], \"rotation\": 90, \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 6, 6, 13 ],\n    \"to\": [ 10, 10, 15 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 4, 16, 0, 14 ], \"rotation\": 180, \"texture\": \"#back\" },\n        \"up\":    { \"uv\": [ 4, 16, 0, 14 ], \"texture\": \"#back\" },\n        \"west\":  { \"uv\": [ 0, 16, 4, 14 ], \"rotation\": 270, \"texture\": \"#back\" },\n        \"east\":  { \"uv\": [ 0, 14, 4, 16 ], \"rotation\": 270, \"texture\": \"#back\" }\n    }\n},\n{\n    \"from\": [ 5, 5, 12 ],\n    \"to\": [ 11, 11, 13 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 0, 15, 6, 16 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 0, 15, 6, 16 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 10, 10, 16, 16 ], \"texture\": \"#back\" },\n        \"south\": { \"uv\": [ 10, 10, 16, 16 ], \"texture\": \"#back\" },\n        \"west\":  { \"uv\": [ 15, 0, 16, 6 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 15, 0, 16, 6 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 6, 6, 11 ],\n    \"to\": [ 10, 10, 12 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 8, 15, 12, 16 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 8, 15, 12, 16 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 11, 11, 15, 15 ], \"texture\": \"#back\" },\n        \"west\":  { \"uv\": [ 15, 8, 16, 12 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 15, 8, 16, 12 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 3, 10, 15 ],\n    \"to\": [ 4, 12, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 12, 16, 11, 15 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 11, 16, 10, 15 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 12, 10, 15 ],\n    \"to\": [ 13, 12, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 12, 16, 11, 15 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 11, 16, 10, 15 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 0, 4, 1, 6 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 3, 4, 15 ],\n    \"to\": [ 4, 6, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 11, 16, 10, 15 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 12, 16, 11, 15 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 1, 2, 2, 4 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 2, 4 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 1, 2, 2, 4 ], \"rotation\": 180, \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 12, 4, 15 ],\n    \"to\": [ 13, 6, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 11, 16, 10, 15 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 12, 16, 11, 15 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 1, 2, 2, 4 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 2, 4 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 0, 4, 1, 6 ], \"rotation\": 180, \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 4, 12, 15 ],\n    \"to\": [ 6, 13, 16 ],\n    \"faces\": {\n        \"up\":    { \"uv\": [ 1, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 5, 2, 7, 3 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 1, 2, 2, 3 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 2, 2, 3, 3 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 10, 12, 15 ],\n    \"to\": [ 12, 13, 16 ],\n    \"faces\": {\n        \"up\":    { \"uv\": [ 1, 2, 3, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 5, 2, 7, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 3, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 2, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 1, 2, 2, 3 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 4, 3, 15 ],\n    \"to\": [ 6, 4, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 1, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 5, 2, 7, 3 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 1, 2, 2, 3 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 2, 2, 3, 3 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 10, 3, 15 ],\n    \"to\": [ 12, 4, 16 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 1, 2, 3, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 5, 2, 7, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 3, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 2, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 1, 2, 2, 3 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 7, 11, 12 ],\n    \"to\": [ 9, 12, 13 ],\n    \"faces\": {\n        \"up\":    { \"uv\": [ 1, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 5, 2, 7, 3 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 1, 2, 2, 3 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 2, 2, 3, 3 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 4, 7, 12 ],\n    \"to\": [ 5, 9, 13 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 12, 16, 11, 15 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 11, 16, 10, 15 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 11, 7, 12 ],\n    \"to\": [ 12, 9, 13 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 12, 16, 11, 15 ], \"texture\": \"#front\" },\n        \"up\":    { \"uv\": [ 11, 16, 10, 15 ], \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 2, 4 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 0, 4, 1, 6 ], \"texture\": \"#front\" }\n    }\n},\n{\n    \"from\": [ 7, 4, 12 ],\n    \"to\": [ 9, 5, 13 ],\n    \"faces\": {\n        \"down\":  { \"uv\": [ 1, 2, 3, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"north\": { \"uv\": [ 5, 2, 7, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"south\": { \"uv\": [ 1, 2, 3, 3 ], \"rotation\": 180, \"texture\": \"#front\" },\n        \"west\":  { \"uv\": [ 2, 2, 3, 3 ], \"texture\": \"#front\" },\n        \"east\":  { \"uv\": [ 1, 2, 2, 3 ], \"texture\": \"#front\" }\n    }\n}\n]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_connector_omni_directional_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_connector_omni_directional\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/part_connector_omni_directional_back\",\n        \"front\": \"integrateddynamics:part/part_connector_omni_directional_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_connector_omni_directional_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_connector_omni_directional\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/part_connector_omni_directional_back\",\n        \"front\": \"integrateddynamics:part/part_connector_omni_directional_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_display_panel_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_active\",\n        \"front\": \"integrateddynamics:part/panel_front_dark\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_display_panel_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_error\",\n        \"front\": \"integrateddynamics:part/panel_front_dark\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_display_panel_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_inactive\",\n        \"front\": \"integrateddynamics:part/panel_front_dark\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_dynamic_light_panel_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_active\",\n        \"front\": \"integrateddynamics:part/panel_front_light\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_dynamic_light_panel_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_error\",\n        \"front\": \"integrateddynamics:part/panel_front_light\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_dynamic_light_panel_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_inactive\",\n        \"front\": \"integrateddynamics:part/panel_front_light\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_effect_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/effect_reader_back\",\n        \"front\": \"integrateddynamics:part/effect_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_effect_writer_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/effect_reader_back\",\n        \"front\": \"integrateddynamics:part/effect_writer_front_active\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_effect_writer_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/effect_reader_back\",\n        \"front\": \"integrateddynamics:part/effect_writer_front_error\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_effect_writer_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/effect_reader_back\",\n        \"front\": \"integrateddynamics:part/effect_writer_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_entity_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/entity_reader_back\",\n        \"front\": \"integrateddynamics:part/entity_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_entity_writer_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/entity_reader_back\",\n        \"front\": \"integrateddynamics:part/entity_writer_front_active\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_entity_writer_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/entity_reader_back\",\n        \"front\": \"integrateddynamics:part/entity_writer_front_error\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_entity_writer_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/entity_reader_back\",\n        \"front\": \"integrateddynamics:part/entity_writer_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_extradimensional_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/extradimensional_reader_back\",\n        \"front\": \"integrateddynamics:part/extradimensional_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_fluid_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/fluid_reader_back\",\n        \"front\": \"integrateddynamics:part/fluid_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_inventory_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/inventory_reader_back\",\n        \"front\": \"integrateddynamics:part/inventory_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_inventory_writer_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/inventory_reader_back\",\n        \"front\": \"integrateddynamics:part/inventory_writer_front_active\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_inventory_writer_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/inventory_reader_back\",\n        \"front\": \"integrateddynamics:part/inventory_writer_front_error\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_inventory_writer_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/inventory_reader_back\",\n        \"front\": \"integrateddynamics:part/inventory_writer_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_machine_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/machine_reader_back\",\n        \"front\": \"integrateddynamics:part/machine_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_machine_writer_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/machine_reader_back\",\n        \"front\": \"integrateddynamics:part/machine_writer_front_active\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_machine_writer_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/machine_reader_back\",\n        \"front\": \"integrateddynamics:part/machine_writer_front_error\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_machine_writer_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/machine_reader_back\",\n        \"front\": \"integrateddynamics:part/machine_writer_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_network_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/network_reader_back\",\n        \"front\": \"integrateddynamics:part/network_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_panel.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part\",\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"elements\": [\n        {\n            \"name\": \"three\",\n            \"from\": [ 6.0, 6.0, 13.0 ],\n            \"to\": [ 10.0, 10.0, 14.0 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 3.0, 3.0, 7.0, 7.0 ] },\n                \"east\": { \"texture\": \"#back\", \"uv\": [ 6.0, 3.0, 7.0, 7.0 ] },\n                \"south\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 4.0, 4.0 ] },\n                \"west\": { \"texture\": \"#back\", \"uv\": [ 3.0, 3.0, 4.0, 7.0 ] },\n                \"up\": { \"texture\": \"#back\", \"uv\": [ 3.0, 3.0, 7.0, 4.0 ] },\n                \"down\": { \"texture\": \"#back\", \"uv\": [ 3.0, 6.0, 7.0, 7.0 ] }\n            }\n        },\n        {\n            \"name\": \"two\",\n            \"from\": [ 4.0, 4.0, 14.0 ],\n            \"to\": [ 12.0, 12.0, 15.0 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 1.0, 1.0, 9.0, 9.0 ] },\n                \"east\": { \"texture\": \"#back\", \"uv\": [ 0.0, 2.0, 2.0, 12.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 4.0, 4.0, 12.0, 12.0 ] },\n                \"west\": { \"texture\": \"#back\", \"uv\": [ 12.0, 2.0, 14.0, 12.0 ] },\n                \"up\": { \"texture\": \"#back\", \"uv\": [ 2.0, 0.0, 12.0, 2.0 ] },\n                \"down\": { \"texture\": \"#back\", \"uv\": [ 2.0, 12.0, 12.0, 14.0 ] }\n            }\n        },\n        {\n            \"name\": \"s1\",\n            \"from\": [ 12.0, 3.0, 14.0 ],\n            \"to\": [ 13.0, 13.0, 16.0 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 9.0, 0.0, 10.0, 10.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 14.0, 3.0, 16.0, 13.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 14.0, 3.0, 13.0, 13.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 12.0, 3.0, 13.0, 13.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 13.0, 3.0, 12.0, 1.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 13.0, 3.0, 12.0, 1.0 ] }\n            }\n        },\n        {\n            \"name\": \"s2\",\n            \"from\": [ 3.0, 3.0, 14.0 ],\n            \"to\": [ 4.0, 13.0, 16.0 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 9.0, 0.0, 10.0, 10.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 3.0, 3.0, 4.0, 13.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 2.0, 3.0, 3.0, 13.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 0.0, 3.0, 2.0, 13.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 3.0, 1.0, 4.0, 3.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 3.0, 1.0, 4.0, 3.0 ] }\n            }\n        },\n        {\n            \"name\": \"s3\",\n            \"from\": [ 4.0, 12.0, 14.0 ],\n            \"to\": [ 12.0, 13.0, 16.0 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 1.0, 0.0, 9.0, 1.0 ] },\n                \"east\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 1.0, 1.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 4.0, 3.0, 12.0, 2.0 ] },\n                \"west\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 1.0, 1.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 4.0, 0.0, 12.0, 2.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 4.0, 3.0, 12.0, 4.0 ] }\n            }\n        },\n        {\n            \"name\": \"s4\",\n            \"from\": [ 4.0, 3.0, 14.0 ],\n            \"to\": [ 12.0, 4.0, 16.0 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 1.0, 9.0, 9.0, 10.0 ] },\n                \"east\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 1.0, 1.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 12.0, 13.0, 4.0, 14.0 ] },\n                \"west\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 1.0, 1.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 4.0, 12.0, 12.0, 13.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 4.0, 14.0, 12.0, 16.0 ] }\n            }\n        }\n    ],\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/cable\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part\",\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"elements\": [\n        {\n            \"name\": \"one\",\n            \"from\": [ 3.0, 3.0, 14.0 ], \n            \"to\": [ 13.0, 13.0, 16.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 10.0, 10.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 0.0, 2.0, 2.0, 12.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 2.0, 2.0, 12.0, 12.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 12.0, 2.0, 14.0, 12.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 2.0, 0.0, 12.0, 2.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 2.0, 12.0, 12.0, 14.0 ] }\n            }\n        },\n        {\n            \"name\": \"two\",\n            \"from\": [ 4.0, 4.0, 13.0 ], \n            \"to\": [ 12.0, 12.0, 14.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 1.0, 1.0, 9.0, 9.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 14.0, 0.0, 15.0, 8.0 ] },\n                \"south\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 8.0, 8.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 14.0, 0.0, 15.0, 8.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 0.0, 14.0, 8.0, 15.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 0.0, 14.0, 8.0, 15.0 ] }\n            }\n        },\n        {\n            \"name\": \"three\",\n            \"from\": [ 5.0, 5.0, 12.0 ], \n            \"to\": [ 11.0, 11.0, 13.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 2.0, 2.0, 8.0, 8.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 15.0, 0.0, 16.0, 6.0 ] },\n                \"south\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 6.0, 6.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 15.0, 0.0, 16.0, 6.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 0.0, 15.0, 6.0, 16.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 0.0, 15.0, 6.0, 16.0 ] }\n            }\n        },\n        {\n            \"name\": \"four\",\n            \"from\": [ 6.0, 6.0, 11.0 ], \n            \"to\": [ 10.0, 10.0, 12.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 3.0, 3.0, 7.0, 7.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 15.0, 8.0, 16.0, 12.0 ] },\n                \"south\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 4.0, 4.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 15.0, 8.0, 16.0, 12.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 8.0, 15.0, 12.0, 16.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 8.0, 15.0, 12.0, 16.0 ] }\n            }\n        }\n    ],\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/cable\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_redstone_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/redstone_reader_back\",\n        \"front\": \"integrateddynamics:part/redstone_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_redstone_writer_active.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/redstone_reader_back\",\n        \"front\": \"integrateddynamics:part/redstone_writer_front_active\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_redstone_writer_error.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/redstone_reader_back\",\n        \"front\": \"integrateddynamics:part/redstone_writer_front_error\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_redstone_writer_inactive.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_writer\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/redstone_reader_back\",\n        \"front\": \"integrateddynamics:part/redstone_writer_front_inactive\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_static_light_panel.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_panel\",\n    \"textures\": {\n        \"back\" : \"integrateddynamics:part/panel_back_inactive\",\n        \"front\": \"integrateddynamics:part/panel_front_light\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_world_reader.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part_reader\",\n    \"textures\": {\n        \"back\": \"integrateddynamics:part/world_reader_back\",\n        \"front\": \"integrateddynamics:part/world_reader_front\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/part_writer.json",
    "content": "{\n    \"parent\": \"integrateddynamics:block/part\",\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"elements\": [\n        {\n            \"name\": \"one\",\n            \"from\": [ 6.0, 6.0, 14.0 ], \n            \"to\": [ 10.0, 10.0, 16.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#front\", \"uv\": [ 0.0, 0.0, 4.0, 4.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 14.0, 8.0, 16.0, 12.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 5.0, 5.0, 9.0, 9.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 14.0, 8.0, 16.0, 12.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 8.0, 14.0, 12.0, 16.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 8.0, 14.0, 12.0, 16.0 ] }\n            }\n        },\n        {\n            \"name\": \"two\",\n            \"from\": [ 5.0, 5.0, 13.0 ], \n            \"to\": [ 11.0, 11.0, 14.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#front\", \"uv\": [ 0.0, 0.0, 6.0, 6.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 15.0, 0.0, 16.0, 6.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 4.0, 4.0, 10.0, 10.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 15.0, 0.0, 16.0, 6.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 0.0, 15.0, 6.0, 16.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 0.0, 15.0, 6.0, 16.0 ] }\n            }\n        },\n        {\n            \"name\": \"three\",\n            \"from\": [ 3.99, 3.99, 11.99 ],\n            \"to\": [ 12.01, 12.01, 13.01 ],\n            \"faces\": {\n                \"north\": { \"texture\": \"#front\", \"uv\": [ 0.0, 0.0, 8.0, 8.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 14.0, 0.0, 15.0, 8.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 3.0, 3.0, 11.0, 11.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 14.0, 0.0, 15.0, 8.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 0.0, 14.0, 8.0, 15.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 0.0, 14.0, 8.0, 15.0 ] }\n            }\n        },\n        {\n            \"name\": \"four\",\n            \"from\": [ 3.0, 3.0, 11.0 ], \n            \"to\": [ 13.0, 13.0, 12.0 ], \n            \"faces\": {\n                \"north\": { \"texture\": \"#back\", \"uv\": [ 0.0, 0.0, 10.0, 10.0 ] },\n                \"east\": { \"texture\": \"#front\", \"uv\": [ 1.0, 2.0, 2.0, 12.0 ] },\n                \"south\": { \"texture\": \"#front\", \"uv\": [ 2.0, 2.0, 12.0, 12.0 ] },\n                \"west\": { \"texture\": \"#front\", \"uv\": [ 12.0, 2.0, 13.0, 12.0 ] },\n                \"up\": { \"texture\": \"#front\", \"uv\": [ 2.0, 1.0, 12.0, 2.0 ] },\n                \"down\": { \"texture\": \"#front\", \"uv\": [ 2.0, 12.0, 12.0, 13.0 ] }\n            }\n        }\n    ],\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/cable\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/proxy.json",
    "content": "{\n    \"parent\": \"block/cube\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/proxy_top\",\n        \"down\": \"integrateddynamics:block/proxy_top\",\n        \"up\": \"integrateddynamics:block/proxy_top\",\n        \"north\": \"integrateddynamics:block/proxy_front\",\n        \"east\": \"integrateddynamics:block/proxy_side\",\n        \"south\": \"integrateddynamics:block/proxy_back\",\n        \"west\": \"integrateddynamics:block/proxy_side\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer.json",
    "content": "{\n    \"parent\": \"block/block\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 0, 2 ],\n            \"to\": [ 16, 1, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 8, 10, 16, 16 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim1\",\n            \"from\": [ 2, 0, 14 ],\n            \"to\": [ 14, 2, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar1\",\n            \"from\": [ 0, 0, 0 ],\n            \"to\": [ 2, 16, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar2\",\n            \"from\": [ 14, 0, 0 ],\n            \"to\": [ 16, 16, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar3\",\n            \"from\": [ 0, 0, 14 ],\n            \"to\": [ 2, 16, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar4\",\n            \"from\": [ 14, 0, 14 ],\n            \"to\": [ 16, 16, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim2\",\n            \"from\": [ 0, 1, 12 ],\n            \"to\": [ 16, 2, 14 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 2, 11.5, 1 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 8.5, 9, 9.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 8.5, 9, 9.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim3\",\n            \"from\": [ 0, 1, 9 ],\n            \"to\": [ 4, 2, 12 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 2, 5.5, 3.5 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 9, 2, 11, 2.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 6, 9, 7.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 6, 9, 7.5, 9.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim4\",\n            \"from\": [ 2, 0, 0 ],\n            \"to\": [ 14, 2, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 8.5, 9.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim5\",\n            \"from\": [ 0, 1, 2 ],\n            \"to\": [ 16, 2, 4 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9, 4.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9, 4.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim6\",\n            \"from\": [ 0, 1, 4 ],\n            \"to\": [ 4, 2, 7 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 3.5, 5.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 4, 2.5, 6, 3 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 4, 9, 5.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 4, 9, 5.5, 9.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim7\",\n            \"from\": [ 12, 1, 4 ],\n            \"to\": [ 16, 2, 7 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 9.5, 3.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 9, 2.5, 11, 3 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 7.5, 0.5, 9, 1 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 7.5, 0, 9, 0.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim8\",\n            \"from\": [ 12, 1, 9 ],\n            \"to\": [ 16, 2, 12 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 9.5, 2, 11.5, 3.5 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 4, 2, 6, 2.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 7.5, 0.5, 9, 1 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 7.5, 0, 9, 0.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_full.json",
    "content": "{\n    \"parent\": \"block/block\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 0, 2 ],\n            \"to\": [ 16, 1, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 8, 10, 16, 16 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 8.5, 9.5, 9 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim1\",\n            \"from\": [ 2, 0, 14 ],\n            \"to\": [ 14, 2, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar1\",\n            \"from\": [ 0, 0, 0 ],\n            \"to\": [ 2, 16, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar2\",\n            \"from\": [ 14, 0, 0 ],\n            \"to\": [ 16, 16, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar3\",\n            \"from\": [ 0, 0, 14 ],\n            \"to\": [ 2, 16, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Pillar4\",\n            \"from\": [ 14, 0, 14 ],\n            \"to\": [ 16, 16, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 1.5, 0, 2.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 0, 0, 1, 8 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim2\",\n            \"from\": [ 0, 1, 12 ],\n            \"to\": [ 16, 2, 14 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 2, 11.5, 1 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 8.5, 9, 9.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 8.5, 9, 9.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim3\",\n            \"from\": [ 0, 1, 9 ],\n            \"to\": [ 4, 2, 12 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 2, 5.5, 3.5 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 9, 2, 11, 2.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 6, 9, 7.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 6, 9, 7.5, 9.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim4\",\n            \"from\": [ 2, 0, 0 ],\n            \"to\": [ 14, 2, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\", \"cullface\": \"down\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 8.5, 9.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim5\",\n            \"from\": [ 0, 1, 2 ],\n            \"to\": [ 16, 2, 4 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 1.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9, 4.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9, 4.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim6\",\n            \"from\": [ 0, 1, 4 ],\n            \"to\": [ 4, 2, 7 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 3.5, 3.5, 5.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 4, 2.5, 6, 3 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 4, 9, 5.5, 9.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 4, 9, 5.5, 9.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim7\",\n            \"from\": [ 12, 1, 4 ],\n            \"to\": [ 16, 2, 7 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 9.5, 3.5, 11.5, 2 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 9, 2.5, 11, 3 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 7.5, 0.5, 9, 1 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 7.5, 0, 9, 0.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Rim8\",\n            \"from\": [ 12, 1, 9 ],\n            \"to\": [ 16, 2, 12 ],\n            \"faces\": {\n                \"up\":    { \"uv\": [ 9.5, 2, 11.5, 3.5 ], \"texture\": \"#texture\" },\n                \"north\": { \"uv\": [ 4, 2, 6, 2.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 7.5, 0.5, 9, 1 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 7.5, 0, 9, 0.5 ], \"texture\": \"#texture\" }\n            }\n        },\n\n\n\n\n\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 14, 2 ],\n            \"to\": [ 16, 16, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 14, 0 ],\n            \"to\": [ 14, 16, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 14, 14 ],\n            \"to\": [ 14, 16, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_1.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 14, 2 ],\n            \"to\": [ 16, 16, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 14, 0 ],\n            \"to\": [ 14, 16, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 14, 14 ],\n            \"to\": [ 14, 16, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_2.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 12, 2 ],\n            \"to\": [ 16, 14, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 12, 0 ],\n            \"to\": [ 14, 14, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 12, 14 ],\n            \"to\": [ 14, 14, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_3.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 10, 2 ],\n            \"to\": [ 16, 12, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 10, 0 ],\n            \"to\": [ 14, 12, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 10, 14 ],\n            \"to\": [ 14, 12, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_4.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 8, 2 ],\n            \"to\": [ 16, 10, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 8, 0 ],\n            \"to\": [ 14, 10, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 8, 14 ],\n            \"to\": [ 14, 10, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_5.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 6, 2 ],\n            \"to\": [ 16, 8, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 6, 0 ],\n            \"to\": [ 14, 8, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 6, 14 ],\n            \"to\": [ 14, 8, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_6.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 4, 2 ],\n            \"to\": [ 16, 6, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 4, 0 ],\n            \"to\": [ 14, 6, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 4, 14 ],\n            \"to\": [ 14, 6, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/squeezer_top_7.json",
    "content": "{\n    \"__comment\": \"Model generated using Mr_crayfish's Model Creator (http://mrcrayfish.com/modelcreator/)\",\n    \"textures\": {\n        \"particle\": \"integrateddynamics:block/menril_log\",\n        \"texture\": \"integrateddynamics:models/squeezer\"\n    },\n    \"elements\": [\n        {\n            \"__comment\": \"Base\",\n            \"from\": [ 0, 2, 2 ],\n            \"to\": [ 16, 4, 14 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 3.5, 1, 11.5, 7 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_left\",\n            \"from\": [ 2, 2, 0 ],\n            \"to\": [ 14, 4, 2 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 1, 10.5, 0 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 0, 10.5, 1 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"north\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        },\n        {\n            \"__comment\": \"Base_right\",\n            \"from\": [ 2, 2, 14 ],\n            \"to\": [ 14, 4, 16 ],\n            \"faces\": {\n                \"down\":  { \"uv\": [ 4.5, 8, 10.5, 7 ], \"texture\": \"#texture\" },\n                \"up\":    { \"uv\": [ 4.5, 7, 10.5, 8 ], \"texture\": \"#texture\", \"cullface\": \"up\" },\n                \"south\": { \"uv\": [ 3.5, 9.5, 9.5, 8.5 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n                \"west\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" },\n                \"east\":  { \"uv\": [ 3.5, 9.5, 4.5, 8.5 ], \"texture\": \"#texture\" }\n            }\n        }\n    ]\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/block/variablestore.json",
    "content": "{\n    \"parent\": \"block/orientable\",\n    \"textures\": {\n        \"top\": \"integrateddynamics:block/variablestore_top\",\n        \"front\": \"integrateddynamics:block/variablestore_front\",\n        \"side\": \"integrateddynamics:block/variablestore_side\"\n    }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/customoverlay/delay.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:customoverlay/delay\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/customoverlay/proxy.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:customoverlay/proxy\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/block_liquid_chorus.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/liquid_chorus\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/block_menril_resin.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_resin\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/bucket_liquid_chorus.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/bucket_liquid_chorus\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/bucket_menril_resin.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/bucket_menril_resin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/cable.json",
    "content": "{\n  \"_comment\": \"Dummy block\",\n  \"parent\": \"integrateddynamics:block/menril_resin\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/coal_generator.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/coal_generator\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_block.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_chorus_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_block_slab.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_chorus_block_slab\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_block_stairs.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_chorus_block_stairs\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_brick.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_chorus_brick\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_brick_slab.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_chorus_brick_slab\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_brick_stairs.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_chorus_brick_stairs\"\n}\ns"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_chorus_chunk.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/crystalized_chorus_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_block.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_menril_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_block_slab.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_menril_block_slab\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_block_stairs.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_menril_block_stairs\"\n}\ns"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_brick.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_menril_brick\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_brick_slab.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_menril_brick_slab\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_brick_stairs.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/crystalized_menril_brick_stairs\"\n}\ns"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/crystalized_menril_chunk.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/crystalized_menril_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/delay.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/delay\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/drying_basin.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/drying_basin\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/energy_battery.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/energy_battery_full\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/energy_battery_creative.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/energy_battery_creative\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/enhancement_offset.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/enhancement_offset\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/facade.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"integrateddynamics:block/facade\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/invisible_light.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/invisible_light\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/labeller.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/labeller\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/logic_director.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/logic_director\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/logic_programmer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/logic_programmer\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/materializer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/materializer\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/mechanical_drying_basin.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/mechanical_drying_basin\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/mechanical_squeezer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/mechanical_squeezer\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_berries.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/menril_berries\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_door.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/door_menril\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_fence.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_fence_inventory\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_fence_gate.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_fence_gate\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_leaves.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_leaves\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_log.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_log\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_log_filled.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_log_filled\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_log_stripped.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_log_stripped\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_planks.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_planks\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_planks_stairs.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_planks_stairs\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_sapling.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:block/menril_sapling\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_slab.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_slab\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_torch.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:block/menril_torch\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_torch_stone.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:block/menril_torch_stone\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_wood.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_wood\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/menril_wood_stripped.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/menril_wood_stripped\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/on_the_dynamics_of_integration.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/on_the_dynamics_of_integration\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_audio_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_audio_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_audio_writer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_audio_writer_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_block_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_block_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_connector_mono_directional.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_connector_mono_directional_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_connector_omni_directional.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_connector_omni_directional_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_display_panel.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_display_panel_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_dynamic_light_panel.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_dynamic_light_panel_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_effect_writer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_effect_writer_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_entity_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_entity_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_entity_writer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_entity_writer_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_extradimensional_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_extradimensional_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_fluid_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_fluid_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_inventory_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_inventory_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_inventory_writer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_inventory_writer_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_machine_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_machine_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_machine_writer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_machine_writer_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_network_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_network_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_redstone_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_redstone_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_redstone_writer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_redstone_writer_inactive\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_static_light_panel.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_static_light_panel\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/part_world_reader.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/part_world_reader\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/portable_logic_programmer.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/portable_logic_programmer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/proto_chorus.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/proto_chorus\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/proxy.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/proxy\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/squeezer.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/squeezer_full\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/variable.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/variable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/variable_transformer_input.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/variable_transformer_input\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/variable_transformer_output.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/variable_transformer_output\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/variablestore.json",
    "content": "{\n  \"parent\": \"integrateddynamics:block/variablestore\"\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/item/wrench.json",
    "content": "{\n  \"parent\": \"cyclopscore:item/flat\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:item/wrench\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/any.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/any\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/block.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/boolean.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/boolean\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/double.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/double\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/entity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/fluidstack.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/ingredients.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/integer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/integer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/itemstack.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/list.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/list\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/long.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/long\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/named.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/named\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/nbt.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/nbt\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/nullable.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/nullable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/number.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/number\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/operator.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/operator\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/recipe.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/string.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/string\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/thaumcraftaspect.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/object\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/models/valuetype/uniquely_named.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"integrateddynamics:valuetype/named\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/sounds.json",
    "content": "{\n    \"effect_page_flipsingle\": {\n        \"sounds\": [\n            \"integrateddynamics:page_flip1\",\n            \"integrateddynamics:page_flip2\",\n            \"integrateddynamics:page_flip3\"\n        ],\n        \"subtitle\": \"subtitles.integrateddynamics.effect.page.flipsingle\"\n    },\n    \"effect_page_flipmultiple\": {\n        \"sounds\": [\n            \"integrateddynamics:pages_flip\"\n        ],\n        \"subtitle\": \"subtitles.integrateddynamics.effect.page.flipmultiple\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/coal_generator_front_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 4,\n\t\"interpolate\": true,\n    \"frames\": [\n\t0,\n\t1,\n\t2,\n\t1,\n\t0,\n\t1,\n\t2,\n\t1,\n\t2\n    ]\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/liquid_chorus_flow.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 3\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/liquid_chorus_still.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 2,\n    \"frames\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9,\n      10,\n      11,\n      12,\n      13,\n      14,\n      15,\n      16,\n      17,\n      18,\n      19,\n      18,\n      17,\n      16,\n      15,\n      14,\n      13,\n      12,\n      11,\n      10,\n      9,\n      8,\n      7,\n      6,\n      5,\n      4,\n      3,\n      2,\n      1\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/mechanical_drying_basin_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 4,\n    \"interpolate\": true,\n    \"frames\": [\n      4,\n      3,\n      2,\n      1,\n      0,\n      1,\n      2\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/mechanical_squeezer_x_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 1,\n    \"frames\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      5,\n      5,\n      5,\n      5,\n      5,\n      5,\n      5,\n      4,\n      4,\n      3,\n      3,\n      2,\n      2,\n      1,\n      1,\n      0\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/menril_resin_flow.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 3\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/integrateddynamics/textures/block/menril_resin_still.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 2,\n    \"frames\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9,\n      10,\n      11,\n      12,\n      13,\n      14,\n      15,\n      16,\n      17,\n      18,\n      19,\n      18,\n      17,\n      16,\n      15,\n      14,\n      13,\n      12,\n      11,\n      10,\n      9,\n      8,\n      7,\n      6,\n      5,\n      4,\n      3,\n      2,\n      1\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/minecraft/atlases/blocks.json",
    "content": "{ \"sources\": [\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:models/drying_basin\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:models/squeezer\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/boolean\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/integer\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/number\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/double\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/nullable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/named\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/list\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/operator\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/object\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/any\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/string\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/nbt\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:valuetype/long\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/boolean/redstone/pulse\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/boolean/redstone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/double/effect/particle\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/redstone/pulse\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/didgeridoo\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/bell\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/hat\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/flute\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/basedrum\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/bit\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/iron_xylophone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/cow_bell\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/bass\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/xylophone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/chime\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/banjo\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/guitar\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/harp\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/snare\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/audio/instrument/pling\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/integer/redstone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/string/audio/sound\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/write/string/audio/text\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/itemstack/entity/itemframecontents\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/nbt/block/tile\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/entity/entity\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/any/network/value\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/block\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/energy/capacity\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/energy/amount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/network/energy/stored\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/network/energy/max\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/world/time\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/world/totaltime\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/fluid/capacity\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/fluid/totalcapacity\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/fluid/totalamount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/long/fluid/amount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/inventory/itemstack\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/recipehandler/recipes\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/entity/players\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/entity/entities\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/world/players\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/fluid/fluidstacks\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/fluid/capacities\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/extradimensional/players\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/list/inventory/itemstacks\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/applicable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/canextract\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/isprovider\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/isempty\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/caninsert\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/isfull\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/isnonempty\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/energy/isreceiver\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/recipehandler/applicable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/network/applicable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/redstone/nonlow\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/redstone/clock\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/redstone/low\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/redstone/high\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/isnight\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/isday\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/mob\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/item\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/weather/thunder\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/weather/clear\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/weather/raining\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/entity\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/animal\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/world/player\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/fluid/nonempty\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/fluid/applicable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/fluid/empty\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/fluid/full\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/machine/isworking\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/machine/isworker\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/machine/canwork\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/machine/haswork\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/inventory/nonempty\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/inventory/applicable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/inventory/empty\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/inventory/full\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/temperature/istemperature\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/boolean/block/block\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/energy/fillratio\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/world/tps\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/fluid/fillratio\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/extradimensional/tps\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/inventory/fillratio\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/temperature/defaulttemperature\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/temperature/temperature\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/temperature/mintemperature\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/double/temperature/maxtemperature\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/entity/itemframerotation\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/network/energy/consumptionrate\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/network/energy/batterycount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/network/elementcount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/redstone/value\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/redstone/comparator\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/world/ticktime\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/world/playercount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/world/daytime\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/world/lightlevel\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/world/raincountdown\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/world/entity\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/fluid/tanks\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/extradimensional/ticktime\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/extradimensional/playercount\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/extradimensional/random\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/didgeridoo\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/bell\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/hat\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/flute\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/basedrum\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/bit\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/iron_xylophone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/cow_bell\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/bass\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/xylophone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/chime\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/banjo\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/guitar\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/harp\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/snare\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/audio/instrument/pling\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/inventory/slots\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/inventory/count\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/inventory/slotsfilled\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/block/light\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/block/posy\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/block/posx\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/integer/block/posz\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/string/world/worldname\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/string/block/biome\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/string/block/dimension\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeoutputsingle\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeinputsingle\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeoutputlist\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeinput\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeoutput\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/recipehandler/recipeinputlist\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/operator/network/variablebyid\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/fluidstack/block\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:aspect/read/fluidstack/fluid\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_mono_directional_back_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_omni_directional_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/machine_writer_front_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_omni_directional_back_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/entity_writer_front_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/empty_writer_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/machine_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/redstone_writer_front_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/empty_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_omni_directional_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/fluid_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/block_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/audio_writer_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/extradimensional_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/entity_writer_front_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/audio_writer_front_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/panel_back_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_mono_directional_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/inventory_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/redstone_writer_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/effect_writer_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/fluid_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/entity_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/audio_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/redstone_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/empty_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/world_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/panel_front_dark\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/inventory_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/redstone_writer_front_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_omni_directional_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/effect_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/world_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/machine_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/inventory_writer_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/network_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/effect_writer_front_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/extradimensional_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/machine_writer_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/effect_writer_front_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/audio_writer_front_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/audio_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/panel_back_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/entity_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/network_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/redstone_reader_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/panel_back_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/effect_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/block_reader_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/inventory_writer_front_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/machine_writer_front_active\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_mono_directional_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/part_connector_mono_directional_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/entity_writer_front_inactive\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/inventory_writer_front_error\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:part/panel_front_light\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:customoverlay/delay\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:customoverlay/proxy\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/logic_programmer_top\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_leaves_fancy\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/variablestore_top\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/coal_generator_side\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/door_menril_lower\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/mechanical_drying_basin_off\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/variablestore_side\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_log_top\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/mechanical_drying_basin_on\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/energy_battery_side_full\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_leaves_fast\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_log_stripped\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_log_filled\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_resin_flow\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/door_menril_upper\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_torch\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/liquid_chorus_flow\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/mechanical_squeezer_x_off\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/proxy_back\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/energy_battery_side_creative\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/variablestore_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_sapling\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/energy_battery_up\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/proxy_top\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/coal_generator_front_on\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/cable\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/mechanical_squeezer_x_on\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/energy_battery_side\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/proxy_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_planks\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/crystalized_menril_brick\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/logic_programmer_side\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_resin_still\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/crystalized_chorus_block\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/delay_side\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_log\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/delay_top\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/liquid_chorus_still\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/crystalized_menril_block\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_torch_stone\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/mechanical_squeezer_y\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/menril_log_top_stripped\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/crystalized_chorus_brick\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/materializer_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/coal_generator_front_off\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/delay_front\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/energy_battery_overlay\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/proxy_side\" },\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:block/facade\" }\n]}\n"
  },
  {
    "path": "src/main/resources/assets/minecraft/atlases/gui.json",
    "content": "{ \"sources\": [\n  { \"type\": \"minecraft:single\", \"resource\": \"integrateddynamics:slot/variable_empty\" }\n]}\n"
  },
  {
    "path": "src/main/resources/data/c/tags/block/storage_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:crystalized_chorus_block\",\n    \"integrateddynamics:crystalized_menril_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/block/stripped_logs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/block/stripped_woods.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_wood_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/block/torches.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_torch\",\n    \"integrateddynamics:menril_torch_stone\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/books.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:on_the_dynamics_of_integration\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/clumps/menril_resin.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:crystalized_menril_chunk\"\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/c/tags/item/clumps.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#c:clumps/menril_resin\"\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/c/tags/item/fruits.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_berries\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/copper.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:copper_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/diamond.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:diamond_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/emerald.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:emerald_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/gold.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:gold_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/iron.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:iron_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/lapis.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:lapis_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/quartz.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:nether_quartz_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/ores/redstone.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:redstone_ores\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/raw_materials/copper.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:raw_copper\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/raw_materials/gold.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:raw_gold\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/raw_materials/iron.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:raw_iron\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/storage_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:crystalized_chorus_block\",\n    \"integrateddynamics:crystalized_menril_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/stripped_logs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/stripped_woods.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_wood_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/tools/wrench.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:wrench\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/item/torches.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_torch\",\n    \"integrateddynamics:menril_torch_stone\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/worldgen/biome/is_cold.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/worldgen/biome/is_coniferous.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/worldgen/biome/is_dense.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/worldgen/biome/is_overworld.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/worldgen/biome/is_wet.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/advanced_network_components/variable_materialization.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:materializer\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.variable_materialization\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.variable_materialization.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/arithmetic_addition\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:variable_created\",\n      \"conditions\": {\n        \"block\": \"integrateddynamics:materializer\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/advanced_network_components/variable_proxying.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:proxy\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.variable_proxying\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.variable_proxying.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/arithmetic_addition\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:variable_created\",\n      \"conditions\": {\n        \"block\": \"integrateddynamics:proxy\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/advanced_operations/item_origin_identification.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:itemstack\",\n          \"value\": {\n            \"v\": {\n              \"stack\": {\n                \"count\": 1,\n                \"id\": \"minecraft:apple\"\n              }\n            }\n          }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.item_origin_identification\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.item_origin_identification.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/logic_programming\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:itemstack_mod\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/advanced_operations/logical_list_building.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:list\",\n          \"value\": {\n            \"proxyName\": \"integrateddynamics:materialized\",\n            \"serialized\": {\n              \"valueType\": \"integrateddynamics:integer\",\n              \"values\": [\n                { \"v\": 1 },\n                { \"v\": 10 },\n                { \"v\": 100 }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.logical_list_building\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.logical_list_building.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/logic_programming\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"value_type\",\n          \"value\": {\n            \"type\": \"serialized\",\n            \"value_type\": \"integrateddynamics:list\",\n            \"value\": \"{proxyName:\\\"integrateddynamics:materialized\\\",serialized:{valueType:\\\"integrateddynamics:integer\\\",values:[{v:1},{v:10},{v:100}]}}\"\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/advanced_operations/what_would_i_be_looking_at.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:block\",\n          \"value\": { \"Name\": \"minecraft:stone\" }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.what_would_i_be_looking_at\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.what_would_i_be_looking_at.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/logic_programming\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:entity_targetblock\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/aspects/redstone_capturing.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:aspect\",\n          \"aspectName\": \"integrateddynamics:read_integer_redstone_value\"\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_capturing\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_capturing.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:basic_network_components/redstone_reading\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_reader_aspect\",\n      \"conditions\": {\n        \"parttype\": \"integrateddynamics:redstone_reader\",\n        \"aspect\": \"integrateddynamics:read_integer_redstone_value\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/aspects/redstone_observement.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:integer\",\n          \"value\": { \"v\": 10 }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_observement\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_observement.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:aspects/redstone_capturing\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"aspect\",\n          \"aspect\": \"integrateddynamics:read_integer_redstone_value\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/aspects/redstone_transmission.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:aspect\",\n          \"aspectName\": \"integrateddynamics:write_integer_redstone\"\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_transmission\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_transmission.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:aspects/redstone_observement\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_writer_aspect\",\n      \"conditions\": {\n        \"parttype\": \"integrateddynamics:redstone_writer\",\n        \"aspect\": \"integrateddynamics:write_integer_redstone\",\n        \"variable\": {\n          \"type\": \"aspect\",\n          \"aspect\": \"integrateddynamics:read_integer_redstone_value\",\n          \"value\": {\n            \"type\": \"serialized\",\n            \"value_type\": \"integrateddynamics:integer\",\n            \"value\": \"{Primitive:15}\"\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/basic_network_components/block_reading.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:part_block_reader\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.block_reading\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.block_reading.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variable_input\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:part_block_reader\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/basic_network_components/inventory_reading.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:part_inventory_reader\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.inventory_reading\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.inventory_reading.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variable_input\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:part_inventory_reader\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/basic_network_components/machine_reading.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:part_machine_reader\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.machine_reading\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.machine_reading.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variable_input\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:part_machine_reader\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/basic_network_components/redstone_reading.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:part_redstone_reader\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_reading\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.redstone_reading.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variable_input\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:part_redstone_reader\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/basic_network_components/value_displaying.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:part_display_panel\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.value_displaying\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.value_displaying.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variable_output\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:part_display_panel\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/challenges/creeper_taming.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"minecraft:creeper_head\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.creeper_taming\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.creeper_taming.desc\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"parent\": \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:entity_health\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"value_type\",\n              \"value\": {\n                \"type\": \"entity\",\n                \"entity\": \"minecraft:creeper\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/challenges/recursive_recursion.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:list\",\n          \"value\": {\n            \"proxyName\": \"integrateddynamics:materialized\",\n            \"serialized\": {\n              \"valueType\": \"integrateddynamics:integer\",\n              \"values\": [\n                { \"v\": 1 },\n                { \"v\": 10 },\n                { \"v\": 100 }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.recursive_recursion\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.recursive_recursion.desc\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"parent\": \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"value_type\",\n          \"value\": {\n            \"type\": \"list\",\n            \"infinite_list\": true\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/challenges/smart_pressure_plate.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:aspect\",\n          \"aspectName\": \"integrateddynamics:read_entity_entity\"\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.smart_pressure_plate\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.smart_pressure_plate.desc\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"parent\": \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_writer_aspect\",\n      \"conditions\": {\n        \"parttype\": \"integrateddynamics:redstone_writer\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:relational_equals\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"operator\",\n              \"operator\": \"integrateddynamics:string_name\",\n              \"input\": {\n                \"0\": {\n                  \"type\": \"aspect\",\n                  \"aspect\": \"integrateddynamics:read_entity_entity\"\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/challenges/sponge_step_sound.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"minecraft:wet_sponge\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.sponge_step_sound\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.sponge_step_sound.desc\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"parent\": \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_writer_aspect\",\n      \"conditions\": {\n        \"parttype\": \"integrateddynamics:audio_writer\",\n        \"aspect\": \"integrateddynamics:write_string_audio_sound\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:block_stepsound\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"value_type\",\n              \"value\": {\n                \"type\": \"serialized\",\n                \"value_type\": \"integrateddynamics:block\",\n                \"value\": \"{v:{Name:\\\"minecraft:wet_sponge\\\"}}\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/challenges/tool_for_obsidian.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"minecraft:obsidian\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.tool_for_obsidian\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.tool_for_obsidian.desc\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"parent\": \"integrateddynamics:advanced_operations/what_would_i_be_looking_at\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:itemstack_canharvest\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"value_type\",\n              \"value\": {\n                \"type\": \"itemstack\",\n                \"item\": {\n                  \"items\": \"minecraft:stone_pickaxe\"\n                }\n              }\n            },\n            \"1\": {\n              \"type\": \"value_type\",\n              \"value\": {\n                \"type\": \"serialized\",\n                \"value_type\": \"integrateddynamics:block\",\n                \"value\": \"{v:{Name:\\\"minecraft:obsidian\\\"}}\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/logic_operations/arithmetic_addition.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:integer\",\n          \"value\": { \"v\": 20 }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.arithmetic_addition\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.arithmetic_addition.desc\"\n    }\n  },\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:materializer\",\n      \"integrateddynamics:proxy\"\n    ]\n  },\n  \"parent\": \"integrateddynamics:logic_operations/constant_definition\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:arithmetic_addition\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/logic_operations/constant_definition.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:integer\",\n          \"value\": { \"v\": 10 }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.constant_definition\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.constant_definition.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/logic_programming\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:variable_created\",\n      \"conditions\": {\n        \"block\": \"integrateddynamics:logic_programmer\",\n        \"variable_facade\": {\n          \"type\": \"value_type\",\n          \"value_type\": \"integrateddynamics:integer\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/logic_operations/logic_programming.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:logic_programmer\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.logic_programming\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.logic_programming.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variables\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:container_gui_open\",\n      \"conditions\": {\n        \"container_class\": \"org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/meneglin_basics/drying.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:drying_basin\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.drying\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.drying.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:meneglin_basics/squeezing\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:drying_basin\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/meneglin_basics/meneglin_discovery.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:menril_log_filled\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.meneglin_discovery\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.meneglin_discovery.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:root\",\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:squeezer\",\n      \"integrateddynamics:drying_basin\"\n    ]\n  },\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\n              \"integrateddynamics:menril_log\"\n            ]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/meneglin_basics/menril_production.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:crystalized_menril_block\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.menril_production\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.menril_production.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:meneglin_basics/squeezing\",\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:wrench\",\n      \"integrateddynamics:variable\",\n      \"integrateddynamics:menril_torch\",\n      \"integrateddynamics:menril_torch_stone\",\n      \"integrateddynamics:crystalized_menril_chunk\",\n      \"integrateddynamics:crystalized_menril_block\",\n      \"integrateddynamics:crystalized_menril_brick\",\n      \"integrateddynamics:crystalized_menril_block_stairs\",\n      \"integrateddynamics:crystalized_menril_brick_stairs\",\n      \"integrateddynamics:menril_planks\",\n      \"integrateddynamics:menril_planks_stairs\"\n    ]\n  },\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\n              \"integrateddynamics:crystalized_menril_block\"\n            ]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/meneglin_basics/squeezing.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:squeezer\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.squeezing\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.squeezing.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:meneglin_basics/meneglin_discovery\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:squeezer\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/nbt/lectern_book.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:nbt\",\n          \"value\": {}\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.lectern_book\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.lectern_book.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:nbt/lectern_data\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:nbt_compound_value_compound\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"aspect\",\n              \"aspect\": \"integrateddynamics:read_nbt_block_tile\"\n            },\n            \"1\": {\n              \"type\": \"value_type\",\n              \"value\": {\n                \"type\": \"serialized\",\n                \"value_type\": \"integrateddynamics:string\",\n                \"value\": \"{Primitive:\\\"Book\\\"}\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/nbt/lectern_book_name.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:nbt\",\n          \"value\": {}\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.lectern_book_name\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.lectern_book_name.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:nbt/lectern_book\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:nbt_path_match_first\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"value_type\",\n              \"value\": {\n                \"type\": \"serialized\",\n                \"value_type\": \"integrateddynamics:string\",\n                \"value\": \"{Primitive:\\\".Book.id\\\"}\"\n              }\n            },\n            \"1\": {\n              \"type\": \"aspect\",\n              \"aspect\": \"integrateddynamics:read_nbt_block_tile\"\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/nbt/lectern_data.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:aspect\",\n          \"aspectName\": \"integrateddynamics:read_nbt_block_tile\"\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.lectern_data\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.lectern_data.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:basic_network_components/block_reading\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"aspect\",\n          \"aspect\": \"integrateddynamics:read_nbt_block_tile\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/network_foundations/cables_logic.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:cable\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.cables_logic\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.cables_logic.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:meneglin_basics/menril_production\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:cable\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/network_foundations/menril_wrenching.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:wrench\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.menril_wrenching\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.menril_wrenching.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:meneglin_basics/menril_production\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:wrench\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/network_foundations/networks_logic.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:cable\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.networks_logic\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.networks_logic.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/cables_logic\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:network_initialized\",\n      \"conditions\": {\n        \"min_cables\": 10\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/network_foundations/variable_input.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable_transformer_input\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.variable_input\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.variable_input.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variables\",\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:part_audio_reader_item\",\n      \"integrateddynamics:part_block_reader_item\",\n      \"integrateddynamics:part_entity_reader_item\",\n      \"integrateddynamics:part_extradimensional_reader_item\",\n      \"integrateddynamics:part_fluid_reader_item\",\n      \"integrateddynamics:part_inventory_reader_item\",\n      \"integrateddynamics:part_machine_reader_item\",\n      \"integrateddynamics:part_network_reader_item\",\n      \"integrateddynamics:part_redstone_reader_item\",\n      \"integrateddynamics:part_world_reader_item\"\n    ]\n  },\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:variable_transformer_input\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/network_foundations/variable_output.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable_transformer_output\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.variable_output\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.variable_output.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:network_foundations/variables\",\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:part_audio_writer_item\",\n      \"integrateddynamics:part_effect_writer_item\",\n      \"integrateddynamics:part_entity_writer_item\",\n      \"integrateddynamics:part_machine_writer_item\",\n      \"integrateddynamics:part_inventory_writer_item\",\n      \"integrateddynamics:part_redstone_writer_item\",\n      \"integrateddynamics:part_static_light_panel_item\",\n      \"integrateddynamics:part_dynamic_light_panel_item\",\n      \"integrateddynamics:part_display_panel_item\"\n    ]\n  },\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:variable_transformer_output\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/network_foundations/variables.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\"\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.variables\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.variables.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:meneglin_basics/menril_production\",\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:variable_transformer\",\n      \"integrateddynamics:variable_transformer_11\",\n      \"integrateddynamics:logic_programmer\",\n      \"integrateddynamics:portable_logic_programmer\",\n      \"integrateddynamics:variablestore\"\n    ]\n  },\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:item_crafted\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"integrateddynamics:variable\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/recipe_handling/recipe_creation.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:valuetype\",\n          \"typeName\": \"integrateddynamics:recipe\",\n          \"value\": {\n            \"output\": {\n              \"minecraft:itemstack\": { \"id\": \"minecraft:stone\", \"Count\": 1 }\n            },\n            \"input\": {\n              \"minecraft:itemstack\": [\n                {\n                  \"type\": 0,\n                  \"val\": [\n                    {\n                      \"prototype\": { \"id\": \"minecraft:stone\", \"Count\": 1 },\n                      \"condition\": 1\n                    }\n                  ]\n                }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.recipe_creation\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.recipe_creation.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:recipe_handling/recipe_reading\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:variable_created\",\n      \"conditions\": {\n        \"block\": \"integrateddynamics:logic_programmer\",\n        \"variable_facade\": {\n          \"type\": \"value_type\",\n          \"value_type\": \"integrateddynamics:recipe\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/recipe_handling/recipe_lookup.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:aspect\",\n          \"aspectName\": \"integrateddynamics:read_operator_recipehandler_recipebyoutput\"\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.recipe_lookup\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.recipe_lookup.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:recipe_handling/recipe_creation\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:operator_apply\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"aspect\",\n              \"aspect\": \"integrateddynamics:read_operator_recipehandler_recipebyoutput\"\n            },\n            \"1\": {\n              \"type\": \"value_type\",\n              \"value_type\": \"integrateddynamics:ingredients\"\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/recipe_handling/recipe_reading.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:aspect\",\n          \"aspectName\": \"integrateddynamics:read_list_recipehandler_recipes\"\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.recipe_reading\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.recipe_reading.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:basic_network_components/machine_reading\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_reader_aspect\",\n      \"conditions\": {\n        \"parttype\": \"integrateddynamics:machine_reader\",\n        \"aspect\": \"integrateddynamics:read_list_recipehandler_recipes\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/root.json",
    "content": "  {\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:cable\"\n    },\n    \"title\": {\n      \"translate\": \"itemGroup.integrateddynamics\"\n    },\n    \"description\": {\n      \"translate\": \"general.integrateddynamics.description\"\n    },\n    \"background\": \"integrateddynamics:gui/advancements/backgrounds/dynamics\",\n    \"show_toast\": false,\n    \"announce_to_chat\": false\n  },\n  \"rewards\": {\n    \"recipes\": [\n      \"integrateddynamics:on_the_dynamics_of_integration\"\n    ]\n  },\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"cyclopscore:mod_item_obtained\",\n      \"conditions\": {\n        \"mod_id\": \"integrateddynamics\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/the_value_of_operators/dynamic_additions.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:operator\",\n          \"operator\": { \"v\": \"integrateddynamics:arithmetic_addition\" },\n          \"variableIds\": [1, 1]\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.dynamic_additions\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.dynamic_additions.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/logic_programming\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:operator_apply\",\n          \"input\": {\n            \"0\": {\n              \"type\": \"operator\",\n              \"operator\": \"integrateddynamics:operator_apply\",\n              \"value_type\": \"integrateddynamics:operator\"\n            },\n            \"1\": {\n              \"type\": \"value_type\",\n              \"value_type\": \"integrateddynamics:integer\"\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/advancement/the_value_of_operators/dynamic_list_filtering.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"id\": \"integrateddynamics:variable\",\n      \"components\": {\n        \"integrateddynamics:variable_facade\": {\n          \"partId\": 1,\n          \"_id\": 1,\n          \"_type\": \"integrateddynamics:operator\",\n          \"operator\": { \"v\": \"integrateddynamics:operator_filter\" },\n          \"variableIds\": [1, 1]\n        }\n      }\n    },\n    \"title\": {\n      \"translate\": \"advancement.integrateddynamics.dynamic_list_filtering\"\n    },\n    \"description\": {\n      \"translate\": \"advancement.integrateddynamics.dynamic_list_filtering.desc\"\n    }\n  },\n  \"parent\": \"integrateddynamics:logic_operations/logic_programming\",\n  \"criteria\": {\n    \"criteria_0\": {\n      \"trigger\": \"integrateddynamics:part_variable_driven\",\n      \"conditions\": {\n        \"part_type\": \"integrateddynamics:display_panel\",\n        \"variable\": {\n          \"type\": \"operator\",\n          \"operator\": \"integrateddynamics:operator_filter\",\n          \"value\": {\n            \"type\": \"serialized\",\n            \"value_type\": \"integrateddynamics:list\",\n            \"value\": \"{proxyName:\\\"integrateddynamics:materialized\\\",serialized:{valueType:\\\"integrateddynamics:integer\\\",values:[{v:10}]}}\"\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/info/on_the_dynamics_of_integration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<section name=\"info_book.integrateddynamics.section.main\">\n    <section name=\"info_book.integrateddynamics.introduction\">\n        <paragraph>info_book.integrateddynamics.introduction.text1</paragraph>\n        <paragraph>info_book.integrateddynamics.introduction.text2</paragraph>\n        <paragraph>info_book.integrateddynamics.introduction.text3</paragraph>\n        <paragraph>info_book.integrateddynamics.introduction.text4</paragraph>\n    </section>\n\n    <section name=\"info_book.integrateddynamics.tutorials\">\n        <section name=\"info_book.integrateddynamics.tutorials.introduction\">\n            <paragraph>info_book.integrateddynamics.tutorials.introduction.text1</paragraph>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.menrilBasics\">\n            <paragraph>info_book.integrateddynamics.tutorials.menrilBasics.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.menrilBasics.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.menrilBasics.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.menrilBasics.text4</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/squeezer</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/drying_basin</appendix>\n            <appendix type=\"integrateddynamics:squeezer\">integrateddynamics:squeezer/base/menril_resin_logs</appendix>\n            <appendix type=\"integrateddynamics:drying_basin\">integrateddynamics:drying_basin/base/crystalized_menril_block</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:meneglin_basics\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:meneglin_basics/meneglin_discovery\" />\n                    <advancement id=\"integrateddynamics:meneglin_basics/squeezing\" />\n                    <advancement id=\"integrateddynamics:meneglin_basics/drying\" />\n                    <advancement id=\"integrateddynamics:meneglin_basics/menril_production\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:menril_log</reward>\n                    <reward type=\"item\" amount=\"10\">integrateddynamics:crystalized_menril_block</reward>\n                    <reward type=\"item\" amount=\"10\">integrateddynamics:crystalized_menril_brick</reward>\n                    <reward type=\"item\" amount=\"20\">integrateddynamics:menril_berries</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.networkFoundations\">\n            <paragraph>info_book.integrateddynamics.tutorials.networkFoundations.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.networkFoundations.text2</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/cable</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/wrench</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variable</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variable_transformer_input</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variable_transformer_output</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:network_foundations\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:network_foundations/cables_logic\" />\n                    <advancement id=\"integrateddynamics:network_foundations/networks_logic\" />\n                    <advancement id=\"integrateddynamics:network_foundations/menril_wrenching\" />\n                    <advancement id=\"integrateddynamics:network_foundations/variables\" />\n                    <advancement id=\"integrateddynamics:network_foundations/variable_input\" />\n                    <advancement id=\"integrateddynamics:network_foundations/variable_output\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:cable</reward>\n                    <reward type=\"item\" amount=\"16\">integrateddynamics:variable_transformer_input</reward>\n                    <reward type=\"item\" amount=\"16\">integrateddynamics:variable_transformer_output</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.basicNetworkComponents\">\n            <paragraph>info_book.integrateddynamics.tutorials.basicNetworkComponents.text1</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_redstone_reader</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_block_reader</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_inventory_reader</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_machine_reader</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_static_light_panel</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_display_panel</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:basic_network_components\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:basic_network_components/redstone_reading\" />\n                    <advancement id=\"integrateddynamics:basic_network_components/block_reading\" />\n                    <advancement id=\"integrateddynamics:basic_network_components/inventory_reading\" />\n                    <advancement id=\"integrateddynamics:basic_network_components/machine_reading\" />\n                    <advancement id=\"integrateddynamics:basic_network_components/value_displaying\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"10\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_display_panel</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.aspects\">\n            <paragraph>info_book.integrateddynamics.tutorials.aspects.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.aspects.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.aspects.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.aspects.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.aspects.text5</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_redstone_writer</appendix>\n            <appendix type=\"integrateddynamics:aspect\">integrateddynamics:read_integer_redstone_value</appendix>\n            <appendix type=\"integrateddynamics:aspect\">integrateddynamics:write_integer_redstone</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:aspects\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:aspects/redstone_capturing\" />\n                    <advancement id=\"integrateddynamics:aspects/redstone_observement\" />\n                    <advancement id=\"integrateddynamics:aspects/redstone_transmission\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_redstone_reader</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_redstone_writer</reward>\n                    <reward type=\"item\" amount=\"16\">integrateddynamics:variable</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.logicOperations\">\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text5</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text6</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text7</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.logicOperations.text8</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_programmer</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_programmer_to_portable</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_programmer_from_portable</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variablestore</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:arithmetic_addition</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:logic_operations\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:logic_operations/logic_programming\" />\n                    <advancement id=\"integrateddynamics:logic_operations/constant_definition\" />\n                    <advancement id=\"integrateddynamics:logic_operations/arithmetic_addition\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:variablestore</reward>\n                    <reward type=\"item\" amount=\"32\">integrateddynamics:variable</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.advancedNetworkComponents\">\n            <paragraph>info_book.integrateddynamics.tutorials.advancedNetworkComponents.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedNetworkComponents.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedNetworkComponents.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedNetworkComponents.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedNetworkComponents.text5</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/materializer</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/proxy</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:advanced_network_components\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:advanced_network_components/variable_materialization\" />\n                    <advancement id=\"integrateddynamics:advanced_network_components/variable_proxying\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:materializer</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:proxy</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.advancedOperations\">\n            <paragraph>info_book.integrateddynamics.tutorials.advancedOperations.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedOperations.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedOperations.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedOperations.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.advancedOperations.text5</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_entity_reader</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:itemstack_mod</appendix>\n            <appendix type=\"integrateddynamics:aspect\">integrateddynamics:read_entity_entity</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:entity_targetblock</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:advanced_operations\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:advanced_operations/logical_list_building\" />\n                    <advancement id=\"integrateddynamics:advanced_operations/item_origin_identification\" />\n                    <advancement id=\"integrateddynamics:advanced_operations/what_would_i_be_looking_at\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"32\">integrateddynamics:variablestore</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.theValueOfOperators\">\n            <paragraph>info_book.integrateddynamics.tutorials.theValueOfOperators.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.theValueOfOperators.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.theValueOfOperators.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.theValueOfOperators.text4</paragraph>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:relational_equals</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:operator_filter</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:operator_apply</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:the_value_of_operators\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:the_value_of_operators/dynamic_additions\" />\n                    <advancement id=\"integrateddynamics:the_value_of_operators/dynamic_list_filtering\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.recipe_handling\">\n            <paragraph>info_book.integrateddynamics.tutorials.recipe_handling.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.recipe_handling.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.recipe_handling.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.recipe_handling.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.recipe_handling.text5</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.recipe_handling.text6</paragraph>\n            <appendix type=\"integrateddynamics:aspect\">integrateddynamics:read_list_recipehandler_recipes</appendix>\n            <appendix type=\"integrateddynamics:aspect\">integrateddynamics:read_operator_recipehandler_recipebyoutput</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:operator_apply</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:recipe_handling\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:recipe_handling/recipe_reading\" />\n                    <advancement id=\"integrateddynamics:recipe_handling/recipe_creation\" />\n                    <advancement id=\"integrateddynamics:recipe_handling/recipe_lookup\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"15\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_machine_reader</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_display_panel</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.nbt\">\n            <paragraph>info_book.integrateddynamics.tutorials.nbt.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.nbt.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.nbt.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.nbt.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.tutorials.nbt.text5</paragraph>\n            <appendix type=\"integrateddynamics:aspect\">integrateddynamics:read_nbt_block_tile</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:nbt_compound_value_compound</appendix>\n            <appendix type=\"integrateddynamics:operator\">integrateddynamics:nbt_path_match_first</appendix>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:nbt\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:nbt/lectern_data\" />\n                    <advancement id=\"integrateddynamics:nbt/lectern_book\" />\n                    <advancement id=\"integrateddynamics:nbt/lectern_book_name\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"30\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_machine_reader</reward>\n                    <reward type=\"item\" amount=\"5\">integrateddynamics:part_display_panel</reward>\n                </rewards>\n            </appendix>\n        </section>\n        <section name=\"info_book.integrateddynamics.tutorials.challenges\">\n            <paragraph>info_book.integrateddynamics.tutorials.challenges.text1</paragraph>\n            <appendix type=\"advancement_rewards\" id=\"integrateddynamics:challenges\">\n                <advancements>\n                    <advancement id=\"integrateddynamics:challenges/creeper_taming\" />\n                    <advancement id=\"integrateddynamics:challenges/sponge_step_sound\" />\n                    <advancement id=\"integrateddynamics:challenges/recursive_recursion\" />\n                    <advancement id=\"integrateddynamics:challenges/tool_for_obsidian\" />\n                    <advancement id=\"integrateddynamics:challenges/smart_pressure_plate\" />\n                </advancements>\n                <rewards>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variable</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:variablestore</reward>\n                    <reward type=\"item\" amount=\"64\">integrateddynamics:part_display_panel</reward>\n                </rewards>\n            </appendix>\n        </section>\n    </section>\n\n    <section name=\"info_book.integrateddynamics.manual\">\n        <section name=\"info_book.integrateddynamics.manual.introduction\">\n            <paragraph>info_book.integrateddynamics.manual.introduction.text1</paragraph>\n        </section>\n        <section name=\"info_book.integrateddynamics.manual.prerequisites\">\n            <section name=\"info_book.integrateddynamics.manual.prerequisites.menril_trees\">\n                <tag>menril_log</tag>\n                <tag>menril_berries</tag>\n                <tag>menril_planks</tag>\n                <tag>menril_sapling</tag>\n                <paragraph>info_book.integrateddynamics.manual.prerequisites.menril_trees.text1</paragraph>\n                <appendix type=\"image\" width=\"120\" height=\"67\">integrateddynamics:textures/gui/infobook/menril_tree.png</appendix>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/menril_planks</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.prerequisites.meneglin_biome\">\n                <paragraph>info_book.integrateddynamics.manual.prerequisites.meneglin_biome.text1</paragraph>\n                <appendix type=\"image\" width=\"120\" height=\"67\">integrateddynamics:textures/gui/infobook/meneglin_biome.png</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.prerequisites.squeezer\">\n                <tag>squeezer</tag>\n                <tag>menril_resin</tag>\n                <tag>liquid_chorus</tag>\n                <tag>proto_chorus</tag>\n                <paragraph>info_book.integrateddynamics.manual.prerequisites.squeezer.text1</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/squeezer</appendix>\n                <appendix_list type=\"integrateddynamics:squeezer\">integrateddynamics:squeezer/base/.*</appendix_list>\n                <appendix_list type=\"integrateddynamics:squeezer\">integrateddynamics:squeezer/convenience/.*</appendix_list>\n                <appendix_list type=\"integrateddynamics:squeezer\">integrateddynamics:squeezer/ore/.*</appendix_list>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/proto_chorus</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.prerequisites.drying_basin\">\n                <tag>drying_basin</tag>\n                <tag>crystalized_menril_block</tag>\n                <tag>crystalized_menril_brick</tag>\n                <tag>crystalized_menril_chunk</tag>\n                <tag>crystalized_chorus_block</tag>\n                <tag>crystalized_chorus_brick</tag>\n                <tag>crystalized_chorus_chunk</tag>\n                <paragraph>info_book.integrateddynamics.manual.prerequisites.drying_basin.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.prerequisites.drying_basin.text2</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/drying_basin</appendix>\n                <appendix_list type=\"integrateddynamics:drying_basin\">integrateddynamics:drying_basin/base/.*</appendix_list>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/crystalized_menril_brick</appendix>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/crystalized_menril_chunk</appendix>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/crystalized_chorus_brick</appendix>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/crystalized_chorus_chunk</appendix>\n                <appendix_list type=\"integrateddynamics:drying_basin\">integrateddynamics:drying_basin/convenience/.*</appendix_list>\n            </section>\n        </section>\n`\n        <section name=\"info_book.integrateddynamics.manual.networks\">\n            <tag>cable</tag>\n            <tag>wrench</tag>\n            <tag>facade</tag>\n            <paragraph>info_book.integrateddynamics.manual.networks.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.networks.text2</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/cable</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/wrench</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/facade</appendix>\n            <appendix type=\"image\" width=\"71\" height=\"67\">integrateddynamics:textures/gui/infobook/network.png</appendix>\n        </section>\n\n        <section name=\"info_book.integrateddynamics.manual.logic\">\n            <section name=\"info_book.integrateddynamics.manual.logic.value_types\">\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.boolean\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.boolean.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.integer\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.integer.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.integer.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.integer.text3</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.double\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.double.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.double.text2</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.long\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.long.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.long.text2</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.string\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.string.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.list\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.list.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.operator\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.operator.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.operator.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.operator.text3</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.nbt\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.nbt.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.nbt.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.nbt.text3</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.block\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.block.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.item\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.item.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.entity\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.entity.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.fluid\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.fluid.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.ingredients\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.ingredients.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.value_types.recipe\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.recipe.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.recipe.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.recipe.text3</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.recipe.text4</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.value_types.recipe.text5</paragraph>\n                </section>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.logic.variables\">\n                <section name=\"info_book.integrateddynamics.manual.logic.variables.variable_card\">\n                    <tag>variable</tag>\n                    <tag>labeller</tag>\n                    <tag>variable_transformer_input</tag>\n                    <tag>variable_transformer_output</tag>\n                    <tag>logic_director</tag>\n                    <paragraph>info_book.integrateddynamics.manual.logic.variables.variable_card.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.variables.variable_card.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.variables.variable_card.text3</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.variables.variable_card.text4</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variable</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/labeller</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variable_transformer_input</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variable_transformer_output</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_director</appendix>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.logic.variables.variable_store\">\n                    <tag>variablestore</tag>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/variablestore</appendix>\n                    <paragraph>info_book.integrateddynamics.manual.logic.variables.variable_store.text1</paragraph>\n                </section>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.logic.advanced\">\n                <section name=\"info_book.integrateddynamics.manual.logic.advanced.nbt_path\">\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text3</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text4</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text5</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text6</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text7</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text8</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text9</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text10</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text11</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text12</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.logic.advanced.nbt_path.text13</paragraph>\n                    <appendix type=\"integrateddynamics:operator\">integrateddynamics:nbt_path_match_first</appendix>\n                    <appendix type=\"integrateddynamics:operator\">integrateddynamics:nbt_path_match_all</appendix>\n                    <appendix type=\"integrateddynamics:operator\">integrateddynamics:nbt_path_test</appendix>\n                </section>\n            </section>\n        </section>\n\n        <section name=\"info_book.integrateddynamics.manual.parts\">\n            <section name=\"info_book.integrateddynamics.manual.parts.introduction\">\n                <paragraph>info_book.integrateddynamics.manual.parts.introduction.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.introduction.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.introduction.text3</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.introduction.text4</paragraph>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.parts.settings\">\n                <paragraph>info_book.integrateddynamics.manual.parts.settings.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.settings.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.settings.text3</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.settings.text4</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.settings.text5</paragraph>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.parts.offsets\">\n                <paragraph>info_book.integrateddynamics.manual.parts.offsets.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.offsets.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.offsets.text3</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.parts.offsets.text4</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/enhancement_offset</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.parts.reader\">\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.introduction\">\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.introduction.text1</paragraph>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.audio\">\n                    <tag>part_audio_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.audio.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_audio_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:audio_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.block\">\n                    <tag>part_block_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.block.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_block_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:block_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.entity\">\n                    <tag>part_entity_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.entity.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_entity_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:entity_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.extra_dimensional\">\n                    <tag>part_extradimensional_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.extra_dimensional.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_extradimensional_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:extradimensional_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.fluid\">\n                    <tag>part_fluid_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.fluid.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_fluid_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:fluid_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.inventory\">\n                    <tag>part_inventory_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.inventory.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_inventory_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:inventory_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.machine\">\n                    <tag>part_machine_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.machine.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_machine_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:machine_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.network\">\n                    <tag>part_network_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.network.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_network_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:network_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.redstone\">\n                    <tag>part_redstone_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.redstone.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_redstone_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:redstone_reader</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.reader.world\">\n                    <tag>part_world_reader</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.reader.world.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_world_reader</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:world_reader</appendix_list>\n                </section>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.parts.writer\">\n                <section name=\"info_book.integrateddynamics.manual.parts.writer.audio\">\n                    <tag>part_audio_writer</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.writer.audio.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_audio_writer</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:audio_writer</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.writer.effect\">\n                    <tag>part_effect_writer</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.writer.effect.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_effect_writer</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:effect_writer</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.writer.entity\">\n                    <tag>part_entity_writer</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.writer.entity.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_entity_writer</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:entity_writer</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.writer.machine\">\n                    <tag>part_machine_writer</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.writer.machine.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_machine_writer</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:machine_writer</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.writer.inventory\">\n                    <tag>part_inventory_writer</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.writer.inventory.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_inventory_writer</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:inventory_writer</appendix_list>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.writer.redstone\">\n                    <tag>part_redstone_writer</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.writer.redstone.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_redstone_writer</appendix>\n                    <appendix_list factory=\"integrateddynamics:part_aspects\">integrateddynamics:redstone_writer</appendix_list>\n                </section>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.parts.other\">\n                <section name=\"info_book.integrateddynamics.manual.parts.other.light\">\n                    <tag>part_static_light_panel</tag>\n                    <tag>part_dynamic_light_panel</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.light.text1</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_static_light_panel</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_dynamic_light_panel</appendix>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.other.display\">\n                    <tag>part_display_panel</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.display.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.display.text2</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_display_panel</appendix>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.other.connector_mono_directional\">\n                    <tag>part_connector_mono_directional</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.connector_mono_directional.text2</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/part_connector_mono_directional</appendix>\n                </section>\n                <section name=\"info_book.integrateddynamics.manual.parts.other.connector_omni_directional\">\n                    <tag>part_connector_omni_directional</tag>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text1</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text2</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text3</paragraph>\n                    <paragraph>info_book.integrateddynamics.manual.parts.other.connector_omni_directional.text4</paragraph>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:special/part_connector_omni_directional</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:special/part_connector_omni_directional_copy</appendix>\n                    <appendix type=\"minecraft:crafting\">integrateddynamics:special/part_connector_omni_directional_join</appendix>\n                </section>\n            </section>\n        </section>\n\n        <section name=\"info_book.integrateddynamics.manual.machines\">\n            <section name=\"info_book.integrateddynamics.manual.machines.materializer\">\n                <tag>materializer</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.materializer.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.materializer.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.materializer.text3</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/materializer</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.machines.proxy\">\n                <tag>proxy</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.proxy.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.proxy.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.proxy.text3</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/proxy</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.machines.delay\">\n                <tag>delay</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.delay.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.delay.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.delay.text3</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/delay</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.machines.energy_battery\">\n                <tag>energy_battery</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.energy_battery.text1</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.energy_battery.text2</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.energy_battery.text3</paragraph>\n                <paragraph>info_book.integrateddynamics.manual.machines.energy_battery.text4</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/energy_battery</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.machines.generator\">\n                <tag>coal_generator</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.generator.text1</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/coal_generator</appendix>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.machines.mechanical_squeezer\">\n                <tag>mechanical_squeezer</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.mechanical_squeezer.text1</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/mechanical_squeezer</appendix>\n                <appendix_list type=\"integrateddynamics:mechanical_squeezer\">integrateddynamics:mechanical_squeezer/base/.*</appendix_list>\n                <appendix_list type=\"integrateddynamics:mechanical_squeezer\">integrateddynamics:mechanical_squeezer/convenience/.*</appendix_list>\n                <appendix_list type=\"integrateddynamics:mechanical_squeezer\">integrateddynamics:mechanical_squeezer/ore/.*</appendix_list>\n            </section>\n            <section name=\"info_book.integrateddynamics.manual.machines.mechanical_drying_basin\">\n                <tag>mechanical_drying_basin</tag>\n                <paragraph>info_book.integrateddynamics.manual.machines.mechanical_drying_basin.text1</paragraph>\n                <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/mechanical_drying_basin</appendix>\n                <appendix_list type=\"integrateddynamics:mechanical_drying_basin\">integrateddynamics:mechanical_drying_basin/base/.*</appendix_list>\n                <appendix_list type=\"integrateddynamics:mechanical_drying_basin\">integrateddynamics:mechanical_drying_basin/convenience/.*</appendix_list>\n            </section>\n        </section>\n\n        <section name=\"info_book.integrateddynamics.manual.logic_programming\">\n            <tag>logic_programmer</tag>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text3</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text4</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text5</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text6</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.logic_programming.text7</paragraph>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_programmer</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_programmer_to_portable</appendix>\n            <appendix type=\"minecraft:crafting\">integrateddynamics:crafting/logic_programmer_from_portable</appendix>\n            <appendix type=\"keybinding\">key.integrateddynamics.logic_programmer_focus_search</appendix>\n            <appendix type=\"keybinding\">key.integrateddynamics.logic_programmer_open_rename</appendix>\n            <appendix_list factory=\"integrateddynamics:operators_output\">*</appendix_list>\n        </section>\n\n        <section name=\"info_book.integrateddynamics.manual.diagnostics\">\n            <paragraph>info_book.integrateddynamics.manual.diagnostics.text1</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.diagnostics.text2</paragraph>\n            <paragraph>info_book.integrateddynamics.manual.diagnostics.text3</paragraph>\n        </section>\n    </section>\n</section>"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/coal_generator.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:coal_generator\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_chorus_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_chorus_block\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_chorus_block_slab.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:block_state_property\",\n                  \"block\": \"integrateddynamics:crystalized_chorus_block_slab\",\n                  \"properties\": {\n                    \"type\": \"double\"\n                  }\n                }\n              ],\n              \"count\": 2\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ],\n          \"name\": \"integrateddynamics:crystalized_chorus_block_slab\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_chorus_block_stairs.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_chorus_block_stairs\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_chorus_brick.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_chorus_brick\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_chorus_brick_slab.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:block_state_property\",\n                  \"block\": \"integrateddynamics:crystalized_chorus_brick_slab\",\n                  \"properties\": {\n                    \"type\": \"double\"\n                  }\n                }\n              ],\n              \"count\": 2\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ],\n          \"name\": \"integrateddynamics:crystalized_chorus_brick_slab\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_chorus_brick_stairs.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_chorus_brick_stairs\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_menril_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_menril_block\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_menril_block_slab.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:block_state_property\",\n                  \"block\": \"integrateddynamics:crystalized_menril_block_slab\",\n                  \"properties\": {\n                    \"type\": \"double\"\n                  }\n                }\n              ],\n              \"count\": 2\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ],\n          \"name\": \"integrateddynamics:crystalized_menril_block_slab\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_menril_block_stairs.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_menril_block_stairs\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_menril_brick.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_menril_brick\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_menril_brick_slab.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:block_state_property\",\n                  \"block\": \"integrateddynamics:crystalized_menril_brick_slab\",\n                  \"properties\": {\n                    \"type\": \"double\"\n                  }\n                }\n              ],\n              \"count\": 2\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ],\n          \"name\": \"integrateddynamics:crystalized_menril_brick_slab\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/crystalized_menril_brick_stairs.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_menril_brick_stairs\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/delay.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:copy_name\",\n              \"source\": \"block_entity\"\n            },\n            {\n              \"function\": \"integrateddynamics:copy_proxy_id\"\n            }\n          ],\n          \"name\": \"integrateddynamics:delay\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        },\n        {\n          \"condition\": \"integrateddynamics:match_wrench\"\n        }\n      ]\n    },\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:delay\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:inverted\",\n          \"term\": {\n            \"condition\": \"integrateddynamics:match_wrench\"\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/drying_basin.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:drying_basin\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/energy_battery.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"integrateddynamics:copy_energy_battery_data\"\n            }\n          ],\n          \"name\": \"integrateddynamics:energy_battery\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/energy_battery_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:energy_battery_creative\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/logic_programmer.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:logic_programmer\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/materializer.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:materializer\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/mechanical_drying_basin.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:copy_name\",\n              \"source\": \"block_entity\"\n            },\n            {\n              \"function\": \"integrateddynamics:copy_mechanical_machine_energy\"\n            },\n            {\n              \"function\": \"integrateddynamics:copy_mechanical_drying_basin_tanks\"\n            }\n          ],\n          \"name\": \"integrateddynamics:mechanical_drying_basin\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/mechanical_squeezer.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:copy_name\",\n              \"source\": \"block_entity\"\n            },\n            {\n              \"function\": \"integrateddynamics:copy_mechanical_machine_energy\"\n            },\n            {\n              \"function\": \"integrateddynamics:copy_mechanical_squeezer_tank\"\n            }\n          ],\n          \"name\": \"integrateddynamics:mechanical_squeezer\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_door.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"conditions\": [\n            {\n              \"condition\": \"minecraft:block_state_property\",\n              \"block\": \"integrateddynamics:menril_door\",\n              \"properties\": {\n                \"half\": \"lower\"\n              }\n            }\n          ],\n          \"name\": \"integrateddynamics:menril_door\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_fence.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_fence\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_fence_gate.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_fence_gate\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_leaves.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"name\": \"integrateddynamics:menril_leaves\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:any_of\",\n                  \"terms\": [\n                    {\n                      \"condition\": \"minecraft:match_tool\",\n                      \"predicate\": {\n                        \"items\": \"minecraft:shears\"\n                      }\n                    },\n                    {\n                      \"condition\": \"minecraft:match_tool\",\n                      \"predicate\": {\n                        \"predicates\": {\n                          \"minecraft:enchantments\": [\n                            {\n                              \"enchantments\": \"minecraft:silk_touch\",\n                              \"levels\": {\n                                \"min\": 1\n                              }\n                            }\n                          ]\n                        }\n                      }\n                    }\n                  ]\n                }\n              ]\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"name\": \"integrateddynamics:menril_sapling\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:survives_explosion\"\n                },\n                {\n                  \"condition\": \"minecraft:table_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"chances\": [\n                    0.025,\n                    0.03125,\n                    0.04,\n                    0.05\n                  ]\n                }\n              ]\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"minecraft:stick\",\n          \"conditions\": [\n            {\n              \"condition\": \"minecraft:table_bonus\",\n              \"enchantment\": \"minecraft:fortune\",\n              \"chances\": [\n                0.02,\n                0.022222223,\n                0.025,\n                0.033333335,\n                0.1\n              ]\n            }\n          ],\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"count\": {\n                \"min\": 1.0,\n                \"max\": 2.0,\n                \"type\": \"minecraft:uniform\"\n              }\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ]\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:inverted\",\n          \"term\": {\n            \"condition\": \"minecraft:any_of\",\n            \"terms\": [\n              {\n                \"condition\": \"minecraft:match_tool\",\n                \"predicate\": {\n                  \"items\": \"minecraft:shears\"\n                }\n              },\n              {\n                \"condition\": \"minecraft:match_tool\",\n                \"predicate\": {\n                  \"predicates\": {\n                    \"minecraft:enchantments\": [\n                      {\n                        \"enchantments\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              }\n            ]\n          }\n        }\n      ]\n    },\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_berries\",\n          \"conditions\": [\n            {\n              \"condition\": \"minecraft:table_bonus\",\n              \"enchantment\": \"minecraft:fortune\",\n              \"chances\": [\n                0.15,\n                0.20,\n                0.25,\n                0.30,\n                0.35\n              ]\n            }\n          ],\n          \"functions\": [\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ]\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:inverted\",\n          \"term\": {\n            \"condition\": \"minecraft:any_of\",\n            \"terms\": [\n              {\n                \"condition\": \"minecraft:match_tool\",\n                \"predicate\": {\n                  \"items\": \"minecraft:shears\"\n                }\n              },\n              {\n                \"condition\": \"minecraft:match_tool\",\n                \"predicate\": {\n                  \"predicates\": {\n                    \"minecraft:enchantments\": [\n                      {\n                        \"enchantments\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              }\n            ]\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_log.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_log\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_log_filled.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"name\": \"integrateddynamics:menril_log_filled\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"predicates\": {\n                      \"minecraft:enchantments\": [\n                        {\n                          \"enchantments\": \"minecraft:silk_touch\",\n                          \"levels\": {\n                            \"min\": 1\n                          }\n                        }\n                      ]\n                    }\n                  }\n                }\n              ]\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"name\": \"integrateddynamics:menril_log\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:inverted\",\n                  \"term\": {\n                    \"condition\": \"minecraft:match_tool\",\n                    \"predicate\": {\n                      \"predicates\": {\n                        \"minecraft:enchantments\": [\n                          {\n                            \"enchantments\": \"minecraft:silk_touch\",\n                            \"levels\": {\n                              \"min\": 1\n                            }\n                          }\n                        ]\n                      }\n                    }\n                  }\n                }\n              ]\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:crystalized_menril_chunk\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"count\": {\n                \"min\": 1.0,\n                \"max\": 2.0,\n                \"type\": \"minecraft:uniform\"\n              }\n            },\n            {\n              \"function\": \"minecraft:apply_bonus\",\n              \"enchantment\": \"minecraft:fortune\",\n              \"formula\": \"minecraft:uniform_bonus_count\",\n              \"parameters\": {\n                \"bonusMultiplier\": 1\n              }\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ],\n          \"conditions\": [\n            {\n              \"condition\": \"minecraft:inverted\",\n              \"term\": {\n                \"condition\": \"minecraft:match_tool\",\n                \"predicate\": {\n                  \"predicates\": {\n                    \"minecraft:enchantments\": [\n                      {\n                        \"enchantments\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              }\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_log_stripped.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_log_stripped\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_planks.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_planks\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_planks_stairs.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_planks_stairs\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_sapling.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_sapling\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_slab.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:set_count\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:block_state_property\",\n                  \"block\": \"integrateddynamics:menril_slab\",\n                  \"properties\": {\n                    \"type\": \"double\"\n                  }\n                }\n              ],\n              \"count\": 2\n            },\n            {\n              \"function\": \"minecraft:explosion_decay\"\n            }\n          ],\n          \"name\": \"integrateddynamics:menril_slab\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_torch.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_torch\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_torch_stone.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_torch_stone\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_torch_stone_wall.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_torch_stone\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_torch_wall.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_torch\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_wood.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_wood\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/menril_wood_stripped.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:menril_wood_stripped\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/proxy.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:copy_name\",\n              \"source\": \"block_entity\"\n            },\n            {\n              \"function\": \"integrateddynamics:copy_proxy_id\"\n            }\n          ],\n          \"name\": \"integrateddynamics:proxy\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        },\n        {\n          \"condition\": \"integrateddynamics:match_wrench\"\n        }\n      ]\n    },\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:proxy\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:inverted\",\n          \"term\": {\n            \"condition\": \"integrateddynamics:match_wrench\"\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/squeezer.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"integrateddynamics:squeezer\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/loot_table/blocks/variablestore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:copy_name\",\n              \"source\": \"block_entity\"\n            }\n          ],\n          \"name\": \"integrateddynamics:variablestore\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/neoforge/biome_modifier/add_feature_menril_overworld.json",
    "content": "{\n  \"type\": \"neoforge:add_features\",\n  \"biomes\": \"#minecraft:is_overworld\",\n  \"features\": \"integrateddynamics:tree_menril_general\",\n  \"step\": \"vegetal_decoration\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/blasting/menril_log_coal.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"ingredient\": \"integrateddynamics:menril_log\",\n  \"result\": {\n    \"id\": \"minecraft:charcoal\"\n  },\n  \"experience\": 0.15,\n  \"cookingtime\": 100\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/blasting/menril_log_filled_coal.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"ingredient\": \"integrateddynamics:menril_log_filled\",\n  \"result\": {\n    \"id\": \"minecraft:charcoal\"\n  },\n  \"experience\": 0.15,\n  \"cookingtime\": 100\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/cable.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"integrateddynamics:cable\",\n  \"pattern\": [\n    \"CSC\",\n    \"CRC\",\n    \"CSC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"S\": \"minecraft:stick\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:cable\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/cable_rotated.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"integrateddynamics:cable\",\n  \"pattern\": [\n    \"CCC\",\n    \"SRS\",\n    \"CCC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"S\": \"minecraft:stick\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:cable\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/coal_generator.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:energy_battery\",\n    \"minecraft:furnace\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:coal_generator\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CCC\",\n    \"CCC\",\n    \"CCC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_chorus_chunk\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_block_slab.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_slab\",\n  \"pattern\": [\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": \"integrateddynamics:crystalized_chorus_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_block_slab\",\n    \"count\": 6\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_block_stairs.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"P  \",\n    \"PP \",\n    \"PPP\"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:crystalized_chorus_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_block_stairs\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_brick.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BB\",\n    \"BB\"\n  ],\n  \"key\": {\n    \"B\": \"integrateddynamics:crystalized_chorus_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_brick\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_brick_slab.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_slab\",\n  \"pattern\": [\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": \"integrateddynamics:crystalized_chorus_brick\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_brick_slab\",\n    \"count\": 6\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_brick_stairs.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"P  \",\n    \"PP \",\n    \"PPP\"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:crystalized_chorus_brick\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_brick_stairs\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_chorus_chunk.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:crystalized_chorus_block\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_chunk\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CCC\",\n    \"CCC\",\n    \"CCC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_block_slab.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_slab\",\n  \"pattern\": [\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": \"integrateddynamics:crystalized_menril_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_block_slab\",\n    \"count\": 6\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_block_stairs.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"P  \",\n    \"PP \",\n    \"PPP\"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:crystalized_menril_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_block_stairs\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_brick.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BB\",\n    \"BB\"\n  ],\n  \"key\": {\n    \"B\": \"integrateddynamics:crystalized_menril_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_brick\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_brick_slab.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_slab\",\n  \"pattern\": [\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": \"integrateddynamics:crystalized_menril_brick\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_brick_slab\",\n    \"count\": 6\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_brick_stairs.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"P  \",\n    \"PP \",\n    \"PPP\"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:crystalized_menril_brick\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_brick_stairs\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/crystalized_menril_chunk.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:crystalized_menril_block\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/delay.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"VCV\",\n    \"LSL\",\n    \"VCV\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable\",\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"S\": \"integrateddynamics:variablestore\",\n    \"L\": \"minecraft:clock\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:delay\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/drying_basin.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"LBL\",\n    \"I I\",\n    \"LBL\"\n  ],\n  \"key\": {\n    \"L\": \"#minecraft:logs\",\n    \"B\": \"#c:dyes/black\",\n    \"I\": \"#c:ingots/iron\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:drying_basin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/energy_battery.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CBC\",\n    \"CRC\",\n    \"CBC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"B\": \"integrateddynamics:crystalized_menril_block\",\n    \"R\": \"#c:storage_blocks/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:energy_battery\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/enhancement_offset.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICO\",\n    \"ICO\",\n    \" D \"\n  ],\n  \"key\": {\n    \"I\": \"integrateddynamics:variable_transformer_input\",\n    \"O\": \"integrateddynamics:variable_transformer_output\",\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"D\": \"#c:gems/diamond\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:enhancement_offset\",\n    \"components\": {\n      \"integrateddynamics:part_enhancement\": 4\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/facade.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" S \",\n    \"SCS\",\n    \" S \"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"S\": \"#c:slime_balls\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:facade\",\n    \"count\": 32\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/labeller.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" CB\",\n    \" CC\",\n    \"C  \"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"B\": \"minecraft:writable_book\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:labeller\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/logic_director.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CMC\",\n    \"CDC\",\n    \"CMC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_chorus_chunk\",\n    \"M\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"D\": \"#c:gems/diamond\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:logic_director\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/logic_programmer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"group\": \"integrateddynamics:logic_programmer\",\n  \"ingredients\": [\n    \"integrateddynamics:crystalized_menril_block\",\n    \"minecraft:crafting_table\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:logic_programmer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/logic_programmer_from_portable.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"group\": \"integrateddynamics:logic_programmer\",\n  \"ingredients\": [\n    \"integrateddynamics:portable_logic_programmer\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:logic_programmer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/logic_programmer_to_portable.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:logic_programmer\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:portable_logic_programmer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/materializer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CVC\",\n    \"CVC\",\n    \"CRC\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable\",\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:materializer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/mechanical_drying_basin.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" O \",\n    \"BSB\",\n    \" O \"\n  ],\n  \"key\": {\n    \"O\": \"#c:obsidians\",\n    \"B\": \"integrateddynamics:energy_battery\",\n    \"S\": \"integrateddynamics:drying_basin\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:mechanical_drying_basin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/mechanical_squeezer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" D \",\n    \"BSB\",\n    \" O \"\n  ],\n  \"key\": {\n    \"D\": \"#c:gems/diamond\",\n    \"O\": \"#c:obsidians\",\n    \"B\": \"integrateddynamics:energy_battery\",\n    \"S\": \"integrateddynamics:squeezer\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:mechanical_squeezer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_door.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PP \",\n    \"PP \",\n    \"PP \"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:menril_planks\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_door\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_fence.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_fence\",\n  \"pattern\": [\n    \"W#W\",\n    \"W#W\"\n  ],\n  \"key\": {\n    \"#\": \"minecraft:stick\",\n    \"W\": \"integrateddynamics:menril_planks\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_fence\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_fence_gate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_fence_gate\",\n  \"pattern\": [\n    \"#W#\",\n    \"#W#\"\n  ],\n  \"key\": {\n    \"#\": \"minecraft:stick\",\n    \"W\": \"integrateddynamics:menril_planks\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_fence_gate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_planks.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"#integrateddynamics:menril_logs\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_planks\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_planks_stairs.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"P  \",\n    \"PP \",\n    \"PPP\"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:menril_planks\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_planks_stairs\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_slab.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"group\": \"wooden_slab\",\n  \"pattern\": [\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": \"integrateddynamics:menril_planks\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_slab\",\n    \"count\": 6\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_torch.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"minecraft:stick\",\n    \"integrateddynamics:menril_berries\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_torch\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_torch_stone.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"group\": \"integrateddynamics:menril_torch_stone\",\n  \"ingredients\": [\n    \"#c:stones\",\n    \"#c:stones\",\n    \"integrateddynamics:menril_berries\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_torch_stone\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_torch_stone_cobble.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"group\": \"integrateddynamics:menril_torch_stone\",\n  \"ingredients\": [\n    \"#c:cobblestones\",\n    \"#c:cobblestones\",\n    \"integrateddynamics:menril_berries\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_torch_stone\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_wood.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_wood\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/menril_wood_stripped.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:menril_log_stripped\",\n    \"integrateddynamics:menril_log_stripped\",\n    \"integrateddynamics:menril_log_stripped\",\n    \"integrateddynamics:menril_log_stripped\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:menril_wood_stripped\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/on_the_dynamics_of_integration.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"minecraft:book\",\n    \"integrateddynamics:crystalized_menril_chunk\",\n    \"integrateddynamics:crystalized_menril_chunk\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:on_the_dynamics_of_integration\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_audio_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\",\n    \" X \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"X\": \"minecraft:note_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_audio_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_audio_writer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\",\n    \" X \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"X\": \"minecraft:note_block\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_audio_writer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_block_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CDC\",\n    \"DVD\",\n    \"CDC\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"C\": \"#c:cobblestones\",\n    \"D\": \"minecraft:dirt\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_block_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_connector_mono_directional.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" O \",\n    \"CCC\",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": \"integrateddynamics:variable_transformer_input\",\n    \"O\": \"integrateddynamics:variable_transformer_output\",\n    \"C\": \"integrateddynamics:cable\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_connector_mono_directional\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_display_panel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" B \",\n    \"PVP\",\n    \" B \"\n  ],\n  \"key\": {\n    \"B\": \"integrateddynamics:menril_berries\",\n    \"P\": \"integrateddynamics:part_static_light_panel\",\n    \"V\": \"integrateddynamics:variable_transformer_output\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_display_panel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_dynamic_light_panel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" R \",\n    \"RPR\",\n    \" R \"\n  ],\n  \"key\": {\n    \"P\": \"integrateddynamics:part_static_light_panel\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_dynamic_light_panel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_effect_writer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\",\n    \" X \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"X\": \"minecraft:fire_charge\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_effect_writer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_entity_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\",\n    \" X \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"X\": \"minecraft:porkchop\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_entity_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_entity_writer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\",\n    \" X \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"X\": \"minecraft:porkchop\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_entity_writer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_extradimensional_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" E \",\n    \"PVP\",\n    \" E \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"P\": \"#c:ender_pearls\",\n    \"E\": \"minecraft:ender_eye\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_extradimensional_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_fluid_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" W \",\n    \"BVB\",\n    \" W \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"W\": \"minecraft:water_bucket\",\n    \"B\": \"minecraft:bucket\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_fluid_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_inventory_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"X\": \"#c:chests/wooden\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_inventory_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_inventory_writer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"X\": \"#c:chests/wooden\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_inventory_writer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_machine_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" F \",\n    \"IVI\",\n    \" C \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"F\": \"minecraft:furnace\",\n    \"C\": \"minecraft:crafting_table\",\n    \"I\": \"#c:ingots/iron\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_machine_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_machine_writer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" F \",\n    \"IVI\",\n    \" C \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"F\": \"minecraft:furnace\",\n    \"C\": \"minecraft:crafting_table\",\n    \"I\": \"#c:ingots/iron\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_machine_writer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_network_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" X \",\n    \"XVX\",\n    \" X \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"X\": \"integrateddynamics:cable\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_network_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_redstone_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" B \",\n    \"RVR\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"B\": \"#c:storage_blocks/redstone\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_redstone_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_redstone_writer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" B \",\n    \"RVR\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"B\": \"#c:storage_blocks/redstone\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_redstone_writer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_static_light_panel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" B \",\n    \"BVR\",\n    \" B \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_output\",\n    \"B\": \"integrateddynamics:menril_berries\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_static_light_panel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/part_world_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"DOD\",\n    \"OVO\",\n    \"DOD\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable_transformer_input\",\n    \"O\": \"#c:obsidians\",\n    \"D\": \"minecraft:dirt\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:part_world_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/proto_chorus.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    \"integrateddynamics:menril_berries\",\n    \"integrateddynamics:menril_berries\",\n    \"#c:ender_pearls\",\n    \"#c:ender_pearls\"\n  ],\n  \"result\": {\n    \"id\": \"integrateddynamics:proto_chorus\",\n    \"count\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/proxy.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"VCV\",\n    \"CSC\",\n    \"VCV\"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable\",\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"S\": \"integrateddynamics:variablestore\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:proxy\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/squeezer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SBS\",\n    \"S S\",\n    \"PIP\"\n  ],\n  \"key\": {\n    \"S\": \"minecraft:stick\",\n    \"B\": \"#c:storage_blocks/iron\",\n    \"P\": \"#minecraft:planks\",\n    \"I\": \"#c:ingots/iron\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:squeezer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/variable.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CCC\",\n    \"CPC\",\n    \"CCC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"P\": \"minecraft:paper\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:variable\",\n    \"count\": 24\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/variable_transformer_input.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" V \",\n    \"CPC\",\n    \" V \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable\",\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"P\": \"minecraft:sticky_piston\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:variable_transformer_input\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/variable_transformer_output.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" V \",\n    \"CPC\",\n    \" V \"\n  ],\n  \"key\": {\n    \"V\": \"integrateddynamics:variable\",\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"P\": \"minecraft:piston\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:variable_transformer_output\",\n    \"count\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/variablestore.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CBC\",\n    \"CWC\",\n    \"CBC\"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"B\": \"integrateddynamics:crystalized_menril_block\",\n    \"W\": \"#c:chests/wooden\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:variablestore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/crafting/wrench.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" CR\",\n    \" CC\",\n    \"C  \"\n  ],\n  \"key\": {\n    \"C\": \"integrateddynamics:crystalized_menril_chunk\",\n    \"R\": \"#c:dusts/redstone\"\n  },\n  \"result\": {\n    \"id\": \"integrateddynamics:wrench\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/base/crystalized_chorus_block.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"integrateddynamics:liquid_chorus\",\n    \"amount\": 1000\n  },\n  \"duration\": 300,\n  \"output_item\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/base/crystalized_menril_block.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"integrateddynamics:menril_resin\",\n    \"amount\": 1000\n  },\n  \"duration\": 150,\n  \"output_item\": {\n    \"id\": \"integrateddynamics:crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/evilcraft_hardened_blood.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 1000\n  },\n  \"duration\": 400,\n  \"output_item\": {\n    \"id\": \"evilcraft:hardened_blood\"\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/evilcraft_red_sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:sand\",\n  \"input_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 10\n  },\n  \"duration\": 40,\n  \"output_item\": {\n    \"id\": \"minecraft:red_sand\"\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_coarse_dirt.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:dirt\",\n  \"fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 100\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:coarse_dirt\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_dead_bush.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"#saplings\",\n  \"duration\": 150,\n  \"output_item\": {\n    \"id\": \"minecraft:dead_bush\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_dried_kelp.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:kelp\",\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:dried_kelp\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_leather.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:rotten_flesh\",\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:leather\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_mud.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:mud\",\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:dirt\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_sponge.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:wet_sponge\",\n  \"duration\": 200,\n  \"output_item\": {\n    \"id\": \"minecraft:sponge\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:clay\",\n  \"duration\": 400,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_black.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:black_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:blue_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_brown.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:brown_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_cyan.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:cyan_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_gray.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:gray_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_green.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:green_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_light_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:light_blue_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_light_gray.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:light_gray_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_lime.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:lime_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_magenta.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:magenta_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_orange.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:orange_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_pink.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:pink_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_purple.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:purple_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_red.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:red_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_white.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:white_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/drying_basin/convenience/minecraft_terracotta_clean_yellow.json",
    "content": "{\n  \"type\": \"integrateddynamics:drying_basin\",\n  \"input_item\": \"minecraft:yellow_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/base/crystalized_chorus_block.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"integrateddynamics:liquid_chorus\",\n    \"amount\": 1000\n  },\n  \"duration\": 30,\n  \"output_item\": {\n    \"id\": \"integrateddynamics:crystalized_chorus_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/base/crystalized_menril_block.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"integrateddynamics:menril_resin\",\n    \"amount\": 1000\n  },\n  \"duration\": 15,\n  \"output_item\": {\n    \"id\": \"integrateddynamics:crystalized_menril_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/evilcraft_hardened_blood.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 1000\n  },\n  \"duration\": 40,\n  \"output_item\": {\n    \"id\": \"evilcraft:hardened_blood\"\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/evilcraft_red_sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:sand\",\n  \"fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 10\n  },\n  \"duration\": 4,\n  \"output_item\": {\n    \"id\": \"minecraft:red_sand\"\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_coarse_dirt.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:dirt\",\n  \"fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 100\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:coarse_dirt\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_dead_bush.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"#saplings\",\n  \"duration\": 15,\n  \"output_item\": {\n    \"id\": \"minecraft:dead_bush\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_dried_kelp.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:kelp\",\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:dried_kelp\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_leather.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:rotten_flesh\",\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:leather\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_magma.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:lava\",\n    \"amount\": 1000\n  },\n  \"duration\": 100,\n  \"output_item\": {\n    \"id\": \"minecraft:magma_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_mud.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:mud\",\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:dirt\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_netherrack.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:magma_block\",\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:netherrack\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_sponge.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:wet_sponge\",\n  \"duration\": 20,\n  \"output_item\": {\n    \"id\": \"minecraft:sponge\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:clay\",\n  \"duration\": 40,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_black.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:black_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:blue_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_brown.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:brown_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_cyan.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:cyan_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_gray.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:gray_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_green.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:green_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_light_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:light_blue_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_light_gray.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:light_gray_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_lime.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:lime_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_magenta.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:magenta_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_orange.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:orange_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_pink.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:pink_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_purple.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:purple_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_red.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:red_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_white.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:white_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_drying_basin/convenience/minecraft_terracotta_clean_yellow.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_drying_basin\",\n  \"input_item\": \"minecraft:yellow_terracotta\",\n  \"input_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10,\n  \"output_item\": {\n    \"id\": \"minecraft:terracotta\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/base/liquid_chorus.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:popped_chorus_fruit\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:liquid_chorus\",\n    \"amount\": 125\n  },\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_chorus_chunk\",\n        \"count\": 2\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_chorus_chunk\"\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_chorus_chunk\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 15\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/base/liquid_chorus_proto.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"integrateddynamics:proto_chorus\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:liquid_chorus\",\n    \"amount\": 125\n  },\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_chorus_chunk\"\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_chorus_chunk\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 15\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/base/menril_resin_logs.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"integrateddynamics:menril_log\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:menril_resin\",\n    \"amount\": 1000\n  },\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_menril_chunk\",\n        \"count\": 2\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_menril_chunk\"\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_menril_chunk\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 15\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/base/menril_resin_planks.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"integrateddynamics:menril_planks\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:menril_resin\",\n    \"amount\": 250\n  },\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"integrateddynamics:crystalized_menril_chunk\"\n      }\n    }\n  ],\n  \"duration\": 15\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/evilcraft_blood_leaves.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"evilcraft:undead_leaves\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"evilcraft:hardened_blood_shard\"\n      }\n    }\n  ],\n  \"output_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 50\n  },\n  \"duration\": 25,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/evilcraft_blood_log.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"evilcraft:undead_log\",\n  \"output_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 100\n  },\n  \"duration\": 25,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/evilcraft_dark_gem.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#evilcraft:dark_ores\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"evilcraft:dark_gem\",\n        \"count\": 2\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"evilcraft:dark_gem_crushed\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 20,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/evilcraft_dark_gem_crushed.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"evilcraft:dark_gem\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"evilcraft:dark_gem_crushed\"\n      }\n    }\n  ],\n  \"duration\": 20,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/evilcraft_poison.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#evilcraft:poisonous\",\n  \"output_fluid\": {\n    \"id\": \"evilcraft:poison\",\n    \"amount\": 250\n  },\n  \"duration\": 25,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/mechanical_squeezer_facade.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer_facade\",\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_blaze_powder.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:blaze_rod\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:blaze_powder\",\n        \"count\": 5\n      }\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_brick.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:bricks\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:brick\",\n        \"count\": 4\n      }\n    }\n  ],\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_black.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:wither_rose\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:black_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:black_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:cornflower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:blue_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:blue_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_green.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:cactus\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 500\n  },\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:green_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:green_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_light_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:blue_orchid\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_blue_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_blue_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_light_gray_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:azure_bluet\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_gray_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_gray_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_light_gray_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:oxeye_daisy\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_gray_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_gray_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_magenta_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:allium\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_magenta_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:lilac\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 8\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_orange.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:orange_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:orange_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:orange_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_pink_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:pink_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_pink_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:peony\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 8\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_pink_3.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:cactus_flower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 1\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 1\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_red_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:red_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_red_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:rose_bush\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 8\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_red_3.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:poppy\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_white_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:lily_of_the_valley\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_white_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:white_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_white_bone.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:bone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 6\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 10\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_yellow_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:dandelion\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_yellow_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:sunflower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 8\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_dye_yellow_3.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:sunflower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 1\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 1\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_honey_bottle_sugar.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:honey_bottle\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\",\n        \"count\": 5\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_lava.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:blaze_powder\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:lava\",\n    \"amount\": 250\n  },\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_lava_magma_cream.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:magma_cream\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:lava\",\n    \"amount\": 250\n  },\n  \"duration\": 100\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_lava_netherrack.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:netherrack\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:lava\",\n    \"amount\": 1000\n  },\n  \"duration\": 750\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:glass_blocks\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sand\"\n      }\n    }\n  ],\n  \"duration\": 10\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_string.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#minecraft:wool\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:string\",\n        \"count\": 4\n      }\n    }\n  ],\n  \"duration\": 5\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_sugar.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:sugar_cane\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_water_ice.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:ice\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 1000\n  },\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_water_mud.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:mud\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:dirt\"\n      }\n    }\n  ],\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_water_snow.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:snow\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 1000\n  },\n  \"duration\": 20\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_water_snowball.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:snowball\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  },\n  \"duration\": 10\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_water_sponge.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:wet_sponge\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sponge\"\n      }\n    }\n  ],\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 50\n  },\n  \"duration\": 15\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/convenience/minecraft_wind_charge.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:breeze_rod\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:wind_charge\",\n        \"count\": 6\n      }\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/coal.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/coal\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:coal\",\n        \"count\": 6\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:coal\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/cobbblestone.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:stone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:cobblestone\"\n      }\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/cracked_stone_bricks.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:stone_bricks\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:cracked_stone_bricks\"\n      }\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/dust_coal.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:coal\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:dusts/coal\"\n      }\n    }\n  ],\n  \"duration\": 10,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:dusts/coal\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/dust_obsidian.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:obsidian\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:dusts/obsidian\",\n        \"count\": 2\n      }\n    }\n  ],\n  \"duration\": 60,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:dusts/obsidian\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/flint.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:gravel\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:flint\"\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:flint\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gem_diamond.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/diamond\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/diamond\"\n      },\n      \"count\": 2\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/diamond\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/diamond\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/diamond\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gem_emerald.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/emerald\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/emerald\"\n      },\n      \"count\": 2\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/emerald\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/emerald\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/emerald\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gem_lapis.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/lapis\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/lapis\"\n      },\n      \"count\": 12\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/lapis\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/lapis\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/lapis\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gem_prismarine.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:prismarine_shard\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/prismarine\"\n      }\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gem_quartz.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/quartz\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/quartz\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/quartz\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/quartz\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/quartz\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/glowstone_dust.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:glowstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:glowstone_dust\",\n        \"count\": 4\n      }\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gold_nugget_blackstone.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:gilded_blackstone\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:nuggets/gold\",\n        \"count\": 4\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:nuggets/gold\",\n        \"count\": 5\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gravel.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:cobblestone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:gravel\"\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:flint\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/gravel_nether.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:blackstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:gravel\",\n        \"count\": 2\n      }\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/magma_cream.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:magma_block\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:magma_cream\",\n        \"count\": 4\n      }\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/netherrite_scrap.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:ancient_debris\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:netherite_scrap\"\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:netherite_scrap\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_aluminum.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/aluminum\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/aluminum\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/aluminum\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/aluminum\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/aluminum\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/aluminum\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_ardite.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/ardite\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/ardite\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/ardite\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/ardite\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/ardite\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/ardite\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_cobalt.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/cobalt\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/cobalt\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/cobalt\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/cobalt\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/cobalt\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/cobalt\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_copper.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/copper\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/copper\",\n        \"count\": 8\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/copper\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/copper\",\n        \"count\": 2\n      },\n      \"chance\": 0.25\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_gold.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/gold\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/gold\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/gold\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/gold\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_iron.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/iron\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/iron\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/iron\"\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/iron\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_lead.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/lead\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/lead\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/lead\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/lead\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/lead\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/lead\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_mithril.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/mithril\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/mithril\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/mithril\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/mithril\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/mithril\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/mithril\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_nickel.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/nickel\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/nickel\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/nickel\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/nickel\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/nickel\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/nickel\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_osmium.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/osmium\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/osmium\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/osmium\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/osmium\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/osmium\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/osmium\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_platinum.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/platinum\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/platinum\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/platinum\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/platinum\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/platinum\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/platinum\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_silver.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/silver\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/silver\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/silver\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/silver\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/silver\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/silver\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_tin.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/tin\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tin\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tin\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tin\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/tin\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/tin\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_tungsten.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/tungsten\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tungsten\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tungsten\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tungsten\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/tungsten\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/tungsten\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_uranium.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/uranium\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/uranium\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/uranium\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/uranium\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/uranium\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/uranium\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/raw_zinc.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/zinc\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/zinc\",\n        \"count\": 3\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/zinc\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/zinc\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/zinc\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/zinc\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/red_sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:red_sandstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_sand\",\n        \"count\": 2\n      }\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/redstone.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"#c:ores/redstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:redstone\",\n        \"count\": 12\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:redstone\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/mechanical_squeezer/ore/sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:mechanical_squeezer\",\n  \"input_item\": \"minecraft:sandstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sand\",\n        \"count\": 2\n      }\n    }\n  ],\n  \"duration\": 30\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/smelting/menril_log_coal.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"ingredient\": \"integrateddynamics:menril_log\",\n  \"result\": {\n    \"id\": \"minecraft:charcoal\"\n  },\n  \"experience\": 0.15,\n  \"cookingtime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/smelting/menril_log_filled_coal.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"ingredient\": \"integrateddynamics:menril_log_filled\",\n  \"result\": {\n    \"id\": \"minecraft:charcoal\"\n  },\n  \"experience\": 0.15,\n  \"cookingtime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/clear_delay.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_nbt_clear\",\n  \"item\": \"integrateddynamics:delay\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/clear_facade.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_nbt_clear\",\n  \"item\": \"integrateddynamics:facade\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/clear_part.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_nbt_clear\",\n  \"item\": \"#integrateddynamics:parts\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/clear_proxy.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_nbt_clear\",\n  \"item\": \"integrateddynamics:proxy\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/clear_variable.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_nbt_clear\",\n  \"item\": \"integrateddynamics:variable\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/combine_batteries.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_energycontainer_combination\",\n  \"item\": \"integrateddynamics:energy_battery\",\n  \"maxCapacity\": 655360000\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/facade.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_facade\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/part_connector_omni_directional.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_shaped_omni_directional\",\n  \"group\": \"integrateddynamics:part_connector_omni_directional\",\n  \"pattern\": [\n    \" L \",\n    \"MCM\",\n    \" L \"\n  ],\n  \"key\": {\n    \"L\": \"integrateddynamics:logic_director\",\n    \"M\": \"integrateddynamics:part_connector_mono_directional\",\n    \"C\": \"integrateddynamics:cable\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/part_connector_omni_directional_copy.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_shaped_omni_directional_3\",\n  \"group\": \"integrateddynamics:part_connector_omni_directional\",\n  \"pattern\": [\n    \" L \",\n    \"MOM\",\n    \" L \"\n  ],\n  \"key\": {\n    \"L\": \"integrateddynamics:logic_director\",\n    \"M\": \"integrateddynamics:part_connector_mono_directional\",\n    \"O\": \"integrateddynamics:part_connector_omni_directional\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/part_connector_omni_directional_join.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_shapeless_omni_directional\",\n  \"group\": \"integrateddynamics:part_connector_omni_directional\",\n  \"ingredients\": [\n    \"integrateddynamics:part_connector_omni_directional\",\n    \"integrateddynamics:part_connector_omni_directional\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/special/variable_copy.json",
    "content": "{\n  \"type\": \"integrateddynamics:crafting_special_variable_copy\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/base/liquid_chorus.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:popped_chorus_fruit\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:liquid_chorus\",\n    \"amount\": 125\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/base/liquid_chorus_proto.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"integrateddynamics:proto_chorus\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:liquid_chorus\",\n    \"amount\": 125\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/base/menril_resin_logs.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"integrateddynamics:menril_log\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:menril_resin\",\n    \"amount\": 1000\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/base/menril_resin_planks.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"integrateddynamics:menril_planks\",\n  \"output_fluid\": {\n    \"id\": \"integrateddynamics:menril_resin\",\n    \"amount\": 250\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/evilcraft_blood_leaves.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"evilcraft:undead_leaves\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"evilcraft:hardened_blood_shard\"\n      },\n      \"chance\": 0.25\n    }\n  ],\n  \"output_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 50\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/evilcraft_blood_log.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"evilcraft:undead_log\",\n  \"output_fluid\": {\n    \"id\": \"evilcraft:blood\",\n    \"amount\": 100\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/evilcraft_dark_gem.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#evilcraft:dark_ores\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"evilcraft:dark_gem\",\n        \"count\": 2\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"evilcraft:dark_gem_crushed\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/evilcraft_dark_gem_crushed.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"evilcraft:dark_gem\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"evilcraft:dark_gem_crushed\"\n      }\n    }\n  ],\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/evilcraft_poison.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#evilcraft:poisonous\",\n  \"output_fluid\": {\n    \"id\": \"evilcraft:poison\",\n    \"amount\": 250\n  },\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:mod_loaded\",\n      \"modid\": \"evilcraft\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_blaze_powder.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:blaze_rod\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:blaze_powder\",\n        \"count\": 3\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:blaze_powder\",\n        \"chance\": 0.5\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_brick.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:bricks\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:brick\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_black.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:wither_rose\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:black_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:cornflower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:blue_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_green.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:cactus\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 500\n  },\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:green_dye\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:green_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_light_blue.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:blue_orchid\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_blue_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_light_gray_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:azure_bluet\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_gray_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_light_gray_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:oxeye_daisy\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:light_gray_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_magenta_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:allium\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_magenta_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:lilac\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:magenta_dye\",\n        \"count\": 8\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_orange.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:orange_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:orange_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_pink_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:pink_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_pink_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:peony\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 8\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_pink_3.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:cactus_flower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:pink_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_red_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:red_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_red_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:rose_bush\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 8\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_red_3.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:poppy\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:red_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_white_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:lily_of_the_valley\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_white_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:white_tulip\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_white_bone.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:bone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 6\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:white_dye\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_yellow_1.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:dandelion\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_yellow_2.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:sunflower\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 8\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_dye_yellow_3.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:wildflowers\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:yellow_dye\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_honey_bottle_sugar.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\":  \"honey_bottle\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\"\n      },\n      \"chance\": 0.75\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_lava.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:blaze_powder\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:lava\",\n    \"amount\": 250\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:glass_blocks\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sand\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_string.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#minecraft:wool\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:string\",\n        \"count\": 2\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:string\"\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:string\"\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_sugar.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:sugar_cane\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:sugar\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_water_ice.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:ice\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 1000\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_water_mud.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:mud\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:dirt\"\n      }\n    }\n  ],\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_water_snow.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:snow\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 1000\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_water_snowball.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:snowball\",\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 250\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_water_sponge.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:wet_sponge\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sponge\"\n      }\n    }\n  ],\n  \"output_fluid\": {\n    \"id\": \"minecraft:water\",\n    \"amount\": 50\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/minecraft_wind_charge.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:breeze_rod\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:wind_charge\",\n        \"count\": 5\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:wind_charge\",\n        \"chance\": 0.5\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/convenience/squeezer_facade.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer_facade\"\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/coal.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/coal\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:coal\",\n        \"count\": 4\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:coal\"\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:coal\"\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/cobbblestone.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\":  \"minecraft:stone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:cobblestone\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/cracked_stone_bricks.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:stone_bricks\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:cracked_stone_bricks\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/dust_coal.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:coal\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:dusts/coal\"\n      }\n    }\n  ],\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:dusts/coal\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/dust_obsidian.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:obsidian\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:dusts/obsidian\"\n      }\n    }\n  ],\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:dusts/obsidian\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/flint.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:gravel\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:flint\"\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:flint\"\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gem_diamond.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/diamond\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/diamond\"\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/diamond\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/diamond\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/diamond\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gem_emerald.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/emerald\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/emerald\"\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/emerald\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/emerald\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/emerald\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gem_lapis.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/lapis\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/lapis\"\n      },\n      \"count\": 8\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/lapis\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/lapis\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/lapis\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/lapis\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gem_quartz.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/quartz\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/quartz\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:gems/quartz\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:gems/quartz\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/quartz\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/glowstone_dust.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:glowstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:glowstone_dust\",\n        \"count\": 4\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gold_nugget_blackstone.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:gilded_blackstone\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:nuggets/gold\",\n        \"count\": 4\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:nuggets/gold\",\n        \"count\": 5\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gravel.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:cobblestone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:gravel\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/gravel_nether.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:blackstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:gravel\",\n        \"count\": 2\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/netherrite_scrap.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\":  \"minecraft:ancient_debris\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:netherite_scrap\"\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:netherite_scrap\"\n      },\n      \"chance\": 0.75\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/quartz.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:nether_quartz_ore\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:quartz\",\n        \"count\": 2\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:quartz\"\n      },\n      \"chance\": 0.5\n    }\n  ],\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:dusts/quartz\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_aluminum.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/aluminum\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/aluminum\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/aluminum\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/aluminum\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/aluminum\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_ardite.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/ardite\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/ardite\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/ardite\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/ardite\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/ardite\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_cobalt.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/cobalt\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/cobalt\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/cobalt\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/cobalt\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/cobalt\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_copper.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/copper\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/copper\",\n        \"count\": 4\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/copper\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/copper\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_gold.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/gold\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/gold\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/gold\"\n      },\n      \"chance\": 0.75\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_iron.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/iron\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/iron\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/iron\"\n      },\n      \"chance\": 0.75\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_lead.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/lead\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/lead\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/lead\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/lead\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/lead\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_mithril.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/mithril\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/mithril\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/mithril\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/mithril\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/mithril\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_nickel.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/nickel\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/nickel\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/nickel\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/nickel\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/nickel\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_osmium.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/osmium\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/osmium\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/osmium\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/osmium\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/osmium\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_platinum.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/platinum\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/platinum\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/platinum\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/platinum\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/platinum\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_silver.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/silver\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/silver\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/silver\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/silver\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/silver\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_tin.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/tin\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tin\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tin\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/tin\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/tin\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_tungsten.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/tungsten\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tungsten\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/tungsten\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/tungsten\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/tungsten\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_uranium.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/uranium\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/uranium\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/uranium\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/uranium\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/uranium\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/raw_zinc.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/zinc\",\n  \"output_items\": [\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/zinc\",\n        \"count\": 2\n      }\n    },\n    {\n      \"tag\": {\n        \"tag\": \"c:raw_materials/zinc\"\n      },\n      \"chance\": 0.75\n    }\n  ],\n  \"duration\": 40,\n  \"neoforge:conditions\": [\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:raw_materials/zinc\"\n      }\n    },\n    {\n      \"type\": \"neoforge:not\",\n      \"value\": {\n        \"type\": \"neoforge:tag_empty\",\n        \"tag\": \"c:ores/zinc\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/redstone.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"#c:ores/redstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:redstone\",\n        \"count\": 8\n      }\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:redstone\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    },\n    {\n      \"item\": {\n        \"id\": \"minecraft:redstone\",\n        \"count\": 2\n      },\n      \"chance\": 0.5\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/recipe/squeezer/ore/sand.json",
    "content": "{\n  \"type\": \"integrateddynamics:squeezer\",\n  \"input_item\": \"minecraft:sandstone\",\n  \"output_items\": [\n    {\n      \"item\": {\n        \"id\": \"minecraft:sand\",\n        \"count\": 2\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/tags/block/menril_logs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\",\n    \"integrateddynamics:menril_log_stripped\",\n    \"integrateddynamics:menril_wood\",\n    \"integrateddynamics:menril_wood_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/tags/item/menril_logs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\",\n    \"integrateddynamics:menril_log_stripped\",\n    \"integrateddynamics:menril_wood\",\n    \"integrateddynamics:menril_wood_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/tags/item/parts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:part_audio_reader\",\n    \"integrateddynamics:part_audio_writer\",\n    \"integrateddynamics:part_block_reader\",\n    \"integrateddynamics:part_connector_mono_directional\",\n    \"integrateddynamics:part_connector_omni_directional\",\n    \"integrateddynamics:part_display_panel\",\n    \"integrateddynamics:part_dynamic_light_panel\",\n    \"integrateddynamics:part_effect_writer\",\n    \"integrateddynamics:part_entity_reader\",\n    \"integrateddynamics:part_entity_writer\",\n    \"integrateddynamics:part_extradimensional_reader\",\n    \"integrateddynamics:part_fluid_reader\",\n    \"integrateddynamics:part_inventory_reader\",\n    \"integrateddynamics:part_inventory_writer\",\n    \"integrateddynamics:part_machine_reader\",\n    \"integrateddynamics:part_machine_writer\",\n    \"integrateddynamics:part_network_reader\",\n    \"integrateddynamics:part_redstone_reader\",\n    \"integrateddynamics:part_redstone_writer\",\n    \"integrateddynamics:part_static_light_panel\",\n    \"integrateddynamics:part_world_reader\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_empty.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_empty_append.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_empty_appendparts.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_empty_remove.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_idle.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_redstoneioclock.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_redstoneioclock_append.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_redstoneioclock_appendparts.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_redstoneioclock_remove.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/performance_redstoneioclockvariables.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": []\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/time_day.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:clock_time\",\n      \"clock\": \"minecraft:overworld\",\n      \"time\": 1000\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/time_day_far.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:clock_time\",\n      \"clock\": \"minecraft:overworld\",\n      \"time\": 241000\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/time_night.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:clock_time\",\n      \"clock\": \"minecraft:overworld\",\n      \"time\": 13000\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/weather_clear.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:weather\",\n      \"weather\": \"clear\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/weather_clear2.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:weather\",\n      \"weather\": \"clear\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/weather_rain.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:weather\",\n      \"weather\": \"rain\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/test_environment/weather_thunder.json",
    "content": "{\n  \"type\": \"minecraft:all_of\",\n  \"definitions\": [\n    {\n      \"type\": \"minecraft:weather\",\n      \"weather\": \"thunder\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/web/diagnostics.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-us\">\n<head>\n  <title>Integrated Dynamics - Network Diagnostics</title>\n  <style>\n    body {\n      font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;\n      background: #b4b4b4;\n    }\n\n    .main {\n      width: 800px;\n      min-width: 700px;\n      margin: auto;\n      padding: 20px;\n      border-radius: 5px;\n      border: solid 2px #1b8bb7;\n      background: white;\n    }\n\n    h1 {\n      text-align: center;\n    }\n\n    .error-dialog-invisible {\n      display: none;\n    }\n    .error-dialog-visible {\n      display: block;\n      width: 100%;\n      background: #ff786e;\n      border: solid 2px #da1c0d;\n      font-weight: bold;\n      padding: 5px;\n    }\n    .error-dialog-visible:before {\n      content: 'Error: ';\n    }\n\n    .table-diagnostics {\n      border-collapse: collapse;\n      width: 100%;\n    }\n    .table-diagnostics thead {\n      font-weight: bold;\n      background: #b9ebff;\n    }\n    .table-diagnostics td {\n      border: 2px solid #1b8bb7;\n      padding: 4px;\n    }\n    .table-diagnostics tr.highlighted {\n      background: #21a6a6;\n    }\n    .table-diagnostics tr {\n      cursor: pointer;\n    }\n    .order-ascending::before {\n      content: '\\21E1 ';\n    }\n    .order-descending::before {\n      content: '\\21E3 ';\n    }\n    .table-diagnostics td.sort:not(.order-ascending):hover::before {\n      content: '\\21E3 ';\n    }\n  </style>\n</head>\n<body>\n  <div class=\"main\">\n    <h1 class=\"localize-gui-integrateddynamics-diagnostics-title\">Integrated Dynamics - Network Diagnostics</h1>\n\n    <div id=\"error-dialog\" class=\"error-dialog-invisible\"></div>\n\n    <h2 class=\"localize-gui-integrateddynamics-diagnostics-parts\">Parts</h2>\n    <table id=\"table-parts\" class=\"table-diagnostics\">\n      <thead>\n      <tr>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-network sort sort-network\">Network</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-cables sort sort-cables\">Cables</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-part sort sort-part\">Part</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-ticktime sort sort-ticktime\">Ticktime (ms)</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-dimension sort sort-dimension\">Dimension</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-position sort sort-position\">Position</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-side sort sort-side\">Side</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-actions\">Actions</td>\n      </tr>\n      </thead>\n      <tbody></tbody>\n    </table>\n\n    <h2 class=\"localize-gui-integrateddynamics-diagnostics-observers\">Storage Observers</h2>\n    <table id=\"table-observers\" class=\"table-diagnostics\">\n      <thead>\n      <tr>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-network sort sort-network\">Network</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-part sort sort-part\">Part</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-ticktime sort sort-ticktime\">Ticktime (ms)</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-dimension sort sort-dimension\">Dimension</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-position sort sort-position\">Position</td>\n        <td class=\"localize-gui-integrateddynamics-diagnostics-table-side sort sort-side\">Side</td>\n      </tr>\n      </thead>\n      <tbody></tbody>\n    </table>\n  </div>\n\n  <script>\n    let partsOrderState = { column: 'ticktime', asc: false };\n    let observersOrderState = { column: 'ticktime', asc: false };\n\n    function updateTable(data, table, columnNames, allowTeleport, orderState) {\n      // Reset table\n      table.textContent = '';\n\n      // Sort rows\n      const compareStrings = orderState.column === 'part' || orderState.column === 'dimension' || orderState.column === 'position' || orderState.column === 'side';\n      data.sort((entryLeft, entryRight) => {\n        if (compareStrings) {\n          return entryLeft[orderState.column].localeCompare(entryRight[orderState.column]) * (orderState.asc ? 1 : -1);\n        } else {\n          return (entryLeft[orderState.column] - entryRight[orderState.column]) * (orderState.asc ? 1 : -1);\n        }\n      });\n\n      for (const entry of data) {\n        const row = document.createElement('tr');\n\n        // Create columns\n        for (const columnName of columnNames) {\n          const column = document.createElement('td');\n          column.textContent = entry[columnName];\n          row.appendChild(column);\n        }\n\n        // Add action buttons\n        if (allowTeleport) {\n          const column = document.createElement('td');\n          const button = document.createElement('button');\n          button.textContent = \"Teleport\";\n          button.classList.add(\"teleport\");\n          button.addEventListener(\"click\", async(event) => {\n            await fetch('teleport', {\n              method: 'POST',\n              body: JSON.stringify(entry),\n            });\n          });\n          column.appendChild(button);\n          row.appendChild(column);\n        }\n\n        // Add highlight logic\n        if (entry.highlighted) {\n          row.classList.add('highlighted');\n          row.addEventListener(\"click\", async(event) => {\n            if (event.target.nodeName !== 'BUTTON') {\n              await fetch('highlightDisable', {\n                method: 'POST',\n                body: JSON.stringify(entry),\n              });\n            }\n          });\n        } else {\n          row.addEventListener(\"click\", async(event) => {\n            if (event.target.nodeName !== 'BUTTON') {\n              await fetch('highlightEnable', {\n                method: 'POST',\n                body: JSON.stringify(entry),\n              });\n            }\n          });\n        }\n\n        table.appendChild(row);\n      }\n    }\n\n    function updateLocalization(localization) {\n      for (const [ key, value ] of Object.entries(localization)) {\n        document.querySelectorAll(\".localize-\" + key.replaceAll('.', '-')).forEach(element => {\n          element.textContent = value;\n        });\n      }\n    }\n\n    async function update() {\n      const errorDialog = document.getElementById('error-dialog');\n\n      try {\n        const data = await (await fetch('data.json')).json();\n        errorDialog.classList.remove('error-dialog-visible');\n\n        // Update localization\n        updateLocalization(data.localization);\n\n        // Update parts\n        updateTable(\n                data.parts,\n                document.querySelector('#table-parts tbody'),\n                ['network', 'cables', 'part', 'ticktime', 'dimension', 'position', 'side'],\n                true,\n                partsOrderState,\n        );\n\n        // Update observers\n        updateTable(\n                data.observers,\n                document.querySelector('#table-observers tbody'),\n                ['network', 'part', 'ticktime', 'dimension', 'position', 'side'],\n                false,\n                observersOrderState,\n        );\n      } catch (error) {\n        errorDialog.textContent = error.message;\n        errorDialog.classList.add('error-dialog-visible');\n      }\n\n      setTimeout(update, 500);\n    }\n    addEventListener(\"load\", (event) => update());\n\n    // Click on table columns to sort\n    document.querySelectorAll(\".sort\").forEach(element => {\n      element.addEventListener(\"click\", () => {\n        const orderState = element.parentElement.parentElement.parentElement.id === 'table-parts' ? partsOrderState : observersOrderState;\n\n        // Check the column we're clicking on\n        let column;\n        for (const clazz of element.classList) {\n          if (clazz.startsWith('sort-')) {\n            column = clazz.substr(5);\n          }\n        }\n\n        // Change the order\n        if (column) {\n          if (orderState.column === column) {\n            // Update order state\n            orderState.asc = !orderState.asc;\n\n            // Indicate order state in table\n            element.classList.remove('order-descending');\n            element.classList.remove('order-ascending');\n            element.classList.add(orderState.asc ? 'order-ascending' : 'order-descending');\n          } else {\n            // Update order state\n            orderState.column = column;\n            orderState.asc = false;\n\n            // Remove order state from other columns\n            for (const columnHeader of element.parentElement.parentElement.parentElement.querySelectorAll('.sort')) {\n              columnHeader.classList.remove('order-descending');\n              columnHeader.classList.remove('order-ascending');\n            }\n\n            // Indicate order state in table\n            element.classList.add('order-descending');\n          }\n        }\n      });\n    });\n  </script>\n\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/worldgen/biome/meneglin.json",
    "content": "{\n  \"__comment\": \"Copied from forest.json\",\n  \"carvers\": [\n    \"minecraft:cave\",\n    \"minecraft:cave_extra_underground\",\n    \"minecraft:canyon\"\n  ],\n  \"downfall\": 0.25,\n  \"effects\": {\n    \"fog_color\": 12638463,\n    \"mood_sound\": {\n      \"block_search_extent\": 8,\n      \"offset\": 2.0,\n      \"sound\": \"minecraft:ambient.cave\",\n      \"tick_delay\": 6000\n    },\n    \"music\": [\n      {\n        \"data\": {\n          \"max_delay\": 24000,\n          \"min_delay\": 12000,\n          \"replace_current_music\": false,\n          \"sound\": \"minecraft:music.overworld.forest\"\n        },\n        \"weight\": 1\n      }\n    ],\n    \"music_volume\": 1.0,\n    \"sky_color\": 11726569,\n    \"water_color\": 4445678,\n    \"water_fog_color\": 5613789,\n    \"grass_color\": 5627304,\n    \"foliage_color\": 8442041\n  },\n  \"features\": [\n    [],\n    [\n      \"minecraft:lake_lava_underground\",\n      \"minecraft:lake_lava_surface\"\n    ],\n    [\n      \"minecraft:amethyst_geode\"\n    ],\n    [\n      \"minecraft:monster_room\",\n      \"minecraft:monster_room_deep\"\n    ],\n    [],\n    [],\n    [\n      \"minecraft:ore_dirt\",\n      \"minecraft:ore_gravel\",\n      \"minecraft:ore_granite_upper\",\n      \"minecraft:ore_granite_lower\",\n      \"minecraft:ore_diorite_upper\",\n      \"minecraft:ore_diorite_lower\",\n      \"minecraft:ore_andesite_upper\",\n      \"minecraft:ore_andesite_lower\",\n      \"minecraft:ore_tuff\",\n      \"minecraft:ore_coal_upper\",\n      \"minecraft:ore_coal_lower\",\n      \"minecraft:ore_iron_upper\",\n      \"minecraft:ore_iron_middle\",\n      \"minecraft:ore_iron_small\",\n      \"minecraft:ore_gold\",\n      \"minecraft:ore_gold_lower\",\n      \"minecraft:ore_redstone\",\n      \"minecraft:ore_redstone_lower\",\n      \"minecraft:ore_diamond\",\n      \"minecraft:ore_diamond_medium\",\n      \"minecraft:ore_diamond_large\",\n      \"minecraft:ore_diamond_buried\",\n      \"minecraft:ore_lapis\",\n      \"minecraft:ore_lapis_buried\",\n      \"minecraft:ore_copper\",\n      \"minecraft:underwater_magma\",\n      \"minecraft:disk_sand\",\n      \"minecraft:disk_clay\",\n      \"minecraft:disk_gravel\"\n    ],\n    [],\n    [\n      \"minecraft:spring_water\",\n      \"minecraft:spring_lava\"\n    ],\n    [\n      \"minecraft:glow_lichen\",\n      \"minecraft:forest_flowers\",\n      \"minecraft:trees_birch_and_oak_leaf_litter\",\n      \"minecraft:patch_bush\",\n      \"minecraft:flower_default\",\n      \"minecraft:patch_grass_forest\",\n      \"minecraft:brown_mushroom_normal\",\n      \"minecraft:red_mushroom_normal\",\n      \"minecraft:patch_pumpkin\",\n      \"minecraft:patch_sugar_cane\",\n      \"minecraft:patch_firefly_bush_near_water\",\n      \n      \"integrateddynamics:tree_menril_meneglin\",\n      \"integrateddynamics:flowers_meneglin\"\n    ],\n    [\n      \"minecraft:freeze_top_layer\"\n    ]\n  ],\n  \"has_precipitation\": true,\n  \"spawn_costs\": {},\n  \"spawners\": {\n    \"ambient\": [\n      {\n        \"type\": \"minecraft:bat\",\n        \"maxCount\": 8,\n        \"minCount\": 8,\n        \"weight\": 10\n      }\n    ],\n    \"axolotls\": [],\n    \"creature\": [\n      {\n        \"type\": \"minecraft:rabbit\",\n        \"maxCount\": 4,\n        \"minCount\": 2,\n        \"weight\": 3\n      }\n    ],\n    \"misc\": [],\n    \"monster\": [\n      {\n        \"type\": \"minecraft:spider\",\n        \"maxCount\": 4,\n        \"minCount\": 4,\n        \"weight\": 100\n      },\n      {\n        \"type\": \"minecraft:zombie\",\n        \"maxCount\": 4,\n        \"minCount\": 4,\n        \"weight\": 95\n      },\n      {\n        \"type\": \"minecraft:zombie_villager\",\n        \"maxCount\": 1,\n        \"minCount\": 1,\n        \"weight\": 5\n      },\n      {\n        \"type\": \"minecraft:skeleton\",\n        \"maxCount\": 4,\n        \"minCount\": 4,\n        \"weight\": 100\n      },\n      {\n        \"type\": \"minecraft:creeper\",\n        \"maxCount\": 4,\n        \"minCount\": 4,\n        \"weight\": 100\n      },\n      {\n        \"type\": \"minecraft:slime\",\n        \"maxCount\": 4,\n        \"minCount\": 4,\n        \"weight\": 100\n      },\n      {\n        \"type\": \"minecraft:enderman\",\n        \"maxCount\": 4,\n        \"minCount\": 1,\n        \"weight\": 10\n      },\n      {\n        \"type\": \"minecraft:witch\",\n        \"maxCount\": 1,\n        \"minCount\": 1,\n        \"weight\": 5\n      }\n    ],\n    \"underground_water_creature\": [\n      {\n        \"type\": \"minecraft:glow_squid\",\n        \"maxCount\": 6,\n        \"minCount\": 4,\n        \"weight\": 10\n      }\n    ],\n    \"water_ambient\": [],\n    \"water_creature\": []\n  },\n  \"temperature\": 0.7\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/worldgen/configured_feature/flowers_meneglin.json",
    "content": "{\n  \"type\": \"minecraft:simple_block\",\n  \"config\": {\n    \"to_place\": {\n      \"type\": \"minecraft:noise_provider\",\n      \"noise\": {\n        \"amplitudes\": [\n          1.0\n        ],\n        \"firstOctave\": 0\n      },\n      \"scale\": 0.020833334,\n      \"seed\": 3456,\n      \"states\": [\n        {\n          \"Name\": \"minecraft:blue_orchid\"\n        },\n        {\n          \"Name\": \"minecraft:oxeye_daisy\"\n        },\n        {\n          \"Name\": \"minecraft:white_tulip\"\n        },\n        {\n          \"Name\": \"minecraft:lily_of_the_valley\"\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/worldgen/configured_feature/tree_menril.json",
    "content": "{\n  \"type\": \"minecraft:tree\",\n  \"config\": {\n    \"decorators\": [],\n    \"dirt_provider\": {\n      \"type\": \"minecraft:simple_state_provider\",\n      \"state\": {\n        \"Name\": \"minecraft:dirt\"\n      }\n    },\n    \"foliage_placer\": {\n      \"type\": \"integrateddynamics:menril\",\n      \"offset\": 0,\n      \"radius\": 2\n    },\n    \"foliage_provider\": {\n      \"type\": \"minecraft:simple_state_provider\",\n      \"state\": {\n        \"Name\": \"integrateddynamics:menril_leaves\",\n        \"Properties\": {\n          \"distance\": \"7\",\n          \"persistent\": \"false\",\n          \"waterlogged\": \"false\"\n        }\n      }\n    },\n    \"force_dirt\": false,\n    \"ignore_vines\": true,\n    \"minimum_size\": {\n      \"type\": \"minecraft:two_layers_feature_size\",\n      \"limit\": 1,\n      \"lower_size\": 0,\n      \"upper_size\": 2\n    },\n    \"trunk_placer\": {\n      \"type\": \"integrateddynamics:menril\",\n      \"base_height\": 5,\n      \"height_rand_a\": 2,\n      \"height_rand_b\": 2,\n      \"trunk_height_wider\": 3\n    },\n    \"trunk_provider\": {\n      \"type\": \"minecraft:weighted_state_provider\",\n      \"entries\": [\n        {\n          \"data\": {\n            \"Name\": \"integrateddynamics:menril_log\",\n            \"Properties\": {\n              \"axis\": \"y\"\n            }\n          },\n          \"weight\": 10\n        },\n        {\n          \"data\": {\n            \"Name\": \"integrateddynamics:menril_log_filled\",\n            \"Properties\": {\n              \"axis\": \"y\"\n            }\n          },\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/worldgen/placed_feature/flowers_meneglin.json",
    "content": "{\n  \"feature\": \"integrateddynamics:flowers_meneglin\",\n  \"placement\": [\n    {\n      \"type\": \"minecraft:count\",\n      \"count\": 3\n    },\n    {\n      \"type\": \"minecraft:rarity_filter\",\n      \"chance\": 2\n    },\n    {\n      \"type\": \"minecraft:in_square\"\n    },\n    {\n      \"type\": \"minecraft:heightmap\",\n      \"heightmap\": \"MOTION_BLOCKING\"\n    },\n    {\n      \"type\": \"minecraft:biome\"\n    },\n    {\n      \"type\": \"minecraft:count\",\n      \"count\": 64\n    },\n    {\n      \"type\": \"minecraft:random_offset\",\n      \"xz_spread\": {\n        \"type\": \"minecraft:trapezoid\",\n        \"max\": 6,\n        \"min\": -6,\n        \"plateau\": 0\n      },\n      \"y_spread\": {\n        \"type\": \"minecraft:trapezoid\",\n        \"max\": 2,\n        \"min\": -2,\n        \"plateau\": 0\n      }\n    },\n    {\n      \"type\": \"minecraft:block_predicate_filter\",\n      \"predicate\": {\n        \"type\": \"minecraft:matching_block_tag\",\n        \"tag\": \"minecraft:air\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/worldgen/placed_feature/tree_menril_general.json",
    "content": "{\n  \"feature\": \"integrateddynamics:tree_menril\",\n  \"placement\": [\n    {\n      \"type\": \"minecraft:count\",\n      \"count\": {\n        \"type\": \"minecraft:weighted_list\",\n        \"distribution\": [\n          {\n            \"data\": 0,\n            \"weight\": 99\n          },\n          {\n            \"data\": 1,\n            \"weight\": 1\n          }\n        ]\n      }\n    },\n    {\n      \"type\": \"minecraft:in_square\"\n    },\n    {\n      \"type\": \"minecraft:surface_water_depth_filter\",\n      \"max_water_depth\": 0\n    },\n    {\n      \"type\": \"minecraft:heightmap\",\n      \"heightmap\": \"OCEAN_FLOOR\"\n    },\n    {\n      \"type\": \"minecraft:biome\"\n    },\n    {\n      \"type\": \"minecraft:block_predicate_filter\",\n      \"predicate\": {\n        \"type\": \"minecraft:would_survive\",\n        \"state\": {\n          \"Name\": \"integrateddynamics:menril_sapling\",\n          \"Properties\": {\n            \"stage\": \"0\"\n          }\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/integrateddynamics/worldgen/placed_feature/tree_menril_meneglin.json",
    "content": "{\n  \"feature\": \"integrateddynamics:tree_menril\",\n  \"placement\": [\n    {\n      \"type\": \"minecraft:count\",\n      \"count\": {\n        \"type\": \"minecraft:weighted_list\",\n        \"distribution\": [\n          {\n            \"data\": 1,\n            \"weight\": 9\n          },\n          {\n            \"data\": 2,\n            \"weight\": 1\n          }\n        ]\n      }\n    },\n    {\n      \"type\": \"minecraft:in_square\"\n    },\n    {\n      \"type\": \"minecraft:surface_water_depth_filter\",\n      \"max_water_depth\": 0\n    },\n    {\n      \"type\": \"minecraft:heightmap\",\n      \"heightmap\": \"OCEAN_FLOOR\"\n    },\n    {\n      \"type\": \"minecraft:biome\"\n    },\n    {\n      \"type\": \"minecraft:block_predicate_filter\",\n      \"predicate\": {\n        \"type\": \"minecraft:would_survive\",\n        \"state\": {\n          \"Name\": \"integrateddynamics:menril_sapling\",\n          \"Properties\": {\n            \"stage\": \"0\"\n          }\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/doors.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_door\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/leaves.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_leaves\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/logs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/logs_that_burn.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/mineable/axe.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:drying_basin\",\n    \"integrateddynamics:menril_door\",\n    \"integrateddynamics:menril_fence\",\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\",\n    \"integrateddynamics:menril_log_stripped\",\n    \"integrateddynamics:menril_planks\",\n    \"integrateddynamics:menril_planks_stairs\",\n    \"integrateddynamics:menril_sapling\",\n    \"integrateddynamics:menril_torch\",\n    \"integrateddynamics:menril_torch_wall\",\n    \"integrateddynamics:menril_wood\",\n    \"integrateddynamics:menril_wood_stripped\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/mineable/pickaxe.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:cable\",\n    \"integrateddynamics:coal_generator\",\n    \"integrateddynamics:energy_battery_creative\",\n    \"integrateddynamics:crystalized_chorus_block\",\n    \"integrateddynamics:crystalized_chorus_block_slab\",\n    \"integrateddynamics:crystalized_chorus_block_stairs\",\n    \"integrateddynamics:crystalized_chorus_brick\",\n    \"integrateddynamics:crystalized_chorus_brick_slab\",\n    \"integrateddynamics:crystalized_chorus_brick_stairs\",\n    \"integrateddynamics:crystalized_menril_block\",\n    \"integrateddynamics:crystalized_menril_block_slab\",\n    \"integrateddynamics:crystalized_menril_block_stairs\",\n    \"integrateddynamics:crystalized_menril_brick\",\n    \"integrateddynamics:crystalized_menril_brick_slab\",\n    \"integrateddynamics:crystalized_menril_brick_stairs\",\n    \"integrateddynamics:delay\",\n    \"integrateddynamics:energy_battery\",\n    \"integrateddynamics:logic_programmer\",\n    \"integrateddynamics:materializer\",\n    \"integrateddynamics:mechanical_drying_basin\",\n    \"integrateddynamics:mechanical_squeezer\",\n    \"integrateddynamics:menril_torch_stone\",\n    \"integrateddynamics:menril_torch_stone_wall\",\n    \"integrateddynamics:proxy\",\n    \"integrateddynamics:squeezer\",\n    \"integrateddynamics:variablestore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/planks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_planks\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/saplings.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_sapling\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/stairs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:crystalized_chorus_block_stairs\",\n    \"integrateddynamics:crystalized_chorus_brick_stairs\",\n    \"integrateddynamics:crystalized_menril_block_stairs\",\n    \"integrateddynamics:crystalized_menril_brick_stairs\",\n    \"integrateddynamics:menril_planks_stairs\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/wooden_doors.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_door\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/wooden_fences.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_fence\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/wooden_slabs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_slab\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/block/wooden_stairs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_planks_stairs\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/bookshelf_books.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:on_the_dynamics_of_integration\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/doors.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_door\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/leaves.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_leaves\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/lectern_books.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:on_the_dynamics_of_integration\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/logs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/logs_that_burn.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_log\",\n    \"integrateddynamics:menril_log_filled\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/planks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_planks\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/saplings.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_sapling\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/stairs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:crystalized_chorus_block_stairs\",\n    \"integrateddynamics:crystalized_chorus_brick_stairs\",\n    \"integrateddynamics:crystalized_menril_block_stairs\",\n    \"integrateddynamics:crystalized_menril_brick_stairs\",\n    \"integrateddynamics:menril_planks_stairs\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/wooden_doors.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_door\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/wooden_fences.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_fence\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/wooden_slabs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_slab\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/item/wooden_stairs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"integrateddynamics:menril_planks_stairs\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/mineshaft.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/ruined_portal_mountain.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/worldgen/biome/is_overworld.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    {\n      \"id\": \"integrateddynamics:meneglin\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/neoforge/data_maps/item/compostables.json",
    "content": "{\n  \"values\": {\n    \"integrateddynamics:menril_sapling\": {\n      \"chance\": 0.3\n    },\n    \"integrateddynamics:menril_leaves\": {\n      \"chance\": 0.3\n    },\n    \"integrateddynamics:menril_berries\": {\n      \"chance\": 0.65\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/mixins.integrateddynamics.json",
    "content": "{\n  \"minVersion\": \"0.8.5\",\n  \"required\": true,\n  \"compatibilityLevel\": \"JAVA_17\",\n  \"package\": \"org.cyclops.integrateddynamics.mixin\",\n  \"mixins\": [\n    \"MixinBlockStateBase\"\n  ],\n  \"setSourceFile\": true\n}"
  },
  {
    "path": "src/main/resources/pack.mcmeta",
    "content": "{\n    \"pack\": {\n        \"description\": \"integrateddynamics resources\",\n        \"pack_format\": 15,\n        \"forge:resource_pack_format\": 15,\n        \"forge:data_pack_format\": 15\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/ModBaseMocked.java",
    "content": "package org.cyclops.integrateddynamics;\n\nimport net.minecraft.world.item.CreativeModeTab;\nimport net.minecraft.world.item.ItemStack;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.cyclops.cyclopscore.config.ConfigHandlerCommon;\nimport org.cyclops.cyclopscore.helper.IModHelpers;\nimport org.cyclops.cyclopscore.helper.LoggerHelper;\nimport org.cyclops.cyclopscore.helper.ModHelpersNeoForge;\nimport org.cyclops.cyclopscore.init.IModBase;\nimport org.cyclops.cyclopscore.init.RegistryManager;\nimport org.cyclops.cyclopscore.modcompat.ModCompatLoader;\nimport org.cyclops.cyclopscore.network.IPacketHandler;\nimport org.cyclops.cyclopscore.proxy.ICommonProxyCommon;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.List;\nimport java.util.function.Supplier;\n\n/**\n * @author rubensworks\n */\npublic class ModBaseMocked implements IModBase {\n    @Override\n    public String getModId() {\n        return \"\";\n    }\n\n    @Override\n    public IModHelpers getModHelpers() {\n        return ModHelpersNeoForge.INSTANCE;\n    }\n\n    @Override\n    public IPacketHandler getPacketHandler() {\n        return null;\n    }\n\n    @Override\n    public ConfigHandlerCommon getConfigHandler() {\n        return null;\n    }\n\n    @Override\n    public LoggerHelper getLoggerHelper() {\n        return null;\n    }\n\n    @Override\n    public RegistryManager getRegistryManager() {\n        return null;\n    }\n\n    @Override\n    public ICommonProxyCommon getProxy() {\n        return null;\n    }\n\n    @Override\n    public ModCompatLoader getModCompatLoader() {\n        return null;\n    }\n\n    @Nullable\n    @Override\n    public CreativeModeTab getDefaultCreativeTab() {\n        return null;\n    }\n\n    @Override\n    public void registerDefaultCreativeTabEntry(Supplier<ItemStack> itemStack, CreativeModeTab.TabVisibility visibility) {\n\n    }\n\n    @Override\n    public List<Pair<Supplier<ItemStack>, CreativeModeTab.TabVisibility>> getDefaultCreativeTabEntries() {\n        return List.of();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyValueType.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.ChatFormatting;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport net.minecraft.world.level.storage.ValueInput;\nimport net.minecraft.world.level.storage.ValueOutput;\nimport org.cyclops.integrateddynamics.Reference;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeClient;\nimport org.cyclops.integrateddynamics.core.logicprogrammer.ValueTypeLPElementBase;\n\nimport java.util.function.Consumer;\n\n/**\n * Dummy value type\n * @author rubensworks\n */\npublic class DummyValueType implements IValueType<DummyValueType.DummyValue> {\n\n    public static final DummyValueType TYPE = new DummyValueType();\n\n    @Override\n    public IValueTypeClient<DummyValue> getClient() {\n        return new IValueTypeClient<>() {};\n    }\n\n    @Override\n    public boolean isCategory() {\n        return false;\n    }\n\n    @Override\n    public boolean isObject() {\n        return false;\n    }\n\n    @Override\n    public DummyValue getDefault() {\n        return null;\n    }\n\n    @Override\n    public String getTypeName() {\n        return \"boolean\";\n    }\n\n    @Override\n    public Identifier getUniqueName() {\n        return Identifier.fromNamespaceAndPath(Reference.MOD_ID, \"dummy\");\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return \"boolean\";\n    }\n\n    @Override\n    public void loadTooltip(Consumer<Component> tooltipAdder, boolean appendOptionalInfo, DummyValue value) {\n\n    }\n\n    @Override\n    public MutableComponent toCompactString(DummyValue value) {\n        return Component.literal(\"dummy\");\n    }\n\n    @Override\n    public int getDisplayColor() {\n        return 0;\n    }\n\n    @Override\n    public ChatFormatting getDisplayColorFormat() {\n        return ChatFormatting.WHITE;\n    }\n\n    @Override\n    public boolean correspondsTo(IValueType<?> valueType) {\n        return false;\n    }\n\n    @Override\n    public void serialize(ValueOutput valueOutput, DummyValue value) {\n\n    }\n\n    @Override\n    public Component canDeserialize(ValueInput valueInput) {\n        return null;\n    }\n\n    @Override\n    public DummyValue deserialize(ValueInput valueInput) {\n        return null;\n    }\n\n    @Override\n    public DummyValue materialize(DummyValue value) {\n        return value;\n    }\n\n    @Override\n    public String toString(DummyValue value) {\n        return \"\";\n    }\n\n    @Override\n    public DummyValue parseString(String value) throws EvaluationException {\n        return DummyValue.of();\n    }\n\n    @Override\n    public ValueTypeLPElementBase createLogicProgrammerElement() {\n        return null;\n    }\n\n    @Override\n    public DummyValue cast(IValue value) throws EvaluationException {\n        return (DummyValue) value;\n    }\n\n    public static class DummyValue extends ValueBase {\n\n        private DummyValue() {\n            super(TYPE);\n        }\n\n        public static DummyValue of() {\n            return new DummyValue();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariable.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariableInvalidateListener;\n\n/**\n * A dummy boolean variable.\n * @author rubensworks\n */\npublic class DummyVariable<V extends IValue> implements IVariable<V> {\n\n    private final IValueType<V> type;\n    private V value;\n    private boolean fetched = false;\n\n    public DummyVariable(IValueType<V> type, V value) {\n        this.type = type;\n        this.value = value;\n    }\n\n    public DummyVariable(IValueType<V> type) {\n        this.type = type;\n        this.value = type.getDefault();\n    }\n\n    @Override\n    public IValueType<V> getType() {\n        return type;\n    }\n\n    @Override\n    public V getValue() {\n        this.fetched = true;\n        return value;\n    }\n\n    @Override\n    public void invalidate() {\n\n    }\n\n    @Override\n    public void addInvalidationListener(IVariableInvalidateListener invalidateListener) {\n\n    }\n\n    @Override\n    public void removeInvalidationListener(IVariableInvalidateListener invalidateListener) {\n\n    }\n\n    public void setValue(V value) {\n        this.value = value;\n    }\n\n    public boolean isFetched() {\n        return this.fetched;\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableBoolean.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy boolean variable.\n * @author rubensworks\n */\npublic class DummyVariableBoolean extends DummyVariable<ValueTypeBoolean.ValueBoolean> {\n\n    public DummyVariableBoolean(ValueTypeBoolean.ValueBoolean value) {\n        super(ValueTypes.BOOLEAN, value);\n    }\n\n    public DummyVariableBoolean() {\n        super(ValueTypes.BOOLEAN);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableDouble.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy double variable.\n * @author rubensworks\n */\npublic class DummyVariableDouble extends DummyVariable<ValueTypeDouble.ValueDouble> {\n\n    public DummyVariableDouble(ValueTypeDouble.ValueDouble value) {\n        super(ValueTypes.DOUBLE, value);\n    }\n\n    public DummyVariableDouble() {\n        super(ValueTypes.DOUBLE);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableInteger.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy integer variable.\n * @author rubensworks\n */\npublic class DummyVariableInteger extends DummyVariable<ValueTypeInteger.ValueInteger> {\n\n    public DummyVariableInteger(ValueTypeInteger.ValueInteger value) {\n        super(ValueTypes.INTEGER, value);\n    }\n\n    public DummyVariableInteger() {\n        super(ValueTypes.INTEGER);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableList.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy string variable.\n * @author rubensworks\n */\npublic class DummyVariableList extends DummyVariable<ValueTypeList.ValueList> {\n\n    public DummyVariableList(ValueTypeList.ValueList value) {\n        super(ValueTypes.LIST, value);\n    }\n\n    public DummyVariableList() {\n        super(ValueTypes.LIST);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableLong.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy long variable (based on {@link DummyVariableInteger}).\n * @author met4000\n */\npublic class DummyVariableLong extends DummyVariable<ValueTypeLong.ValueLong> {\n\n    public DummyVariableLong(ValueTypeLong.ValueLong value) {\n        super(ValueTypes.LONG, value);\n    }\n\n    public DummyVariableLong() {\n        super(ValueTypes.LONG);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableNamed.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy string variable.\n * @author rubensworks\n */\npublic class DummyVariableNamed extends DummyVariable<ValueTypeString.ValueString> {\n\n    public DummyVariableNamed(ValueTypeString.ValueString value) {\n        super(ValueTypes.STRING, value);\n    }\n\n    public DummyVariableNamed() {\n        super(ValueTypes.STRING);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableNbt.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy NBT variable.\n * @author rubensworks\n */\npublic class DummyVariableNbt extends DummyVariable<ValueTypeNbt.ValueNbt> {\n\n    public DummyVariableNbt(ValueTypeNbt.ValueNbt value) {\n        super(ValueTypes.NBT, value);\n    }\n\n    public DummyVariableNbt() {\n        super(ValueTypes.NBT);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableOperator.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy boolean variable.\n * @author rubensworks\n */\npublic class DummyVariableOperator extends DummyVariable<ValueTypeOperator.ValueOperator> {\n\n    public DummyVariableOperator(ValueTypeOperator.ValueOperator value) {\n        super(ValueTypes.OPERATOR, value);\n    }\n\n    public DummyVariableOperator() {\n        super(ValueTypes.OPERATOR);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/DummyVariableString.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\n/**\n * Dummy string variable.\n * @author rubensworks\n */\npublic class DummyVariableString extends DummyVariable<ValueTypeString.ValueString> {\n\n    public DummyVariableString(ValueTypeString.ValueString value) {\n        super(ValueTypes.STRING, value);\n    }\n\n    public DummyVariableString() {\n        super(ValueTypes.STRING);\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestArithmeticOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different integer operators.\n *\n * @author rubensworks\n */\npublic class TestArithmeticOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger im10;\n    private DummyVariableInteger i10;\n    private DummyVariableInteger i15;\n\n    private DummyVariableDouble d0;\n    private DummyVariableDouble d1;\n    private DummyVariableDouble dm10;\n    private DummyVariableDouble d10;\n    private DummyVariableDouble d15;\n\n    @BeforeEach\n    public void before() {\n        ValueCastMappings.load();\n\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        im10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-10));\n        i10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10));\n        i15 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(15));\n\n        d0 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0));\n        d1 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(1));\n        dm10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(-10));\n        d10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(10));\n        d15 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(15));\n    }\n\n    /**\n     * ----------------------------------- ADD -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticAddInteger() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 + 10 = 20\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(20));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 + 10 = 10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(10));\n\n        IValue res3 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 + 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 + 10 = 0\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(0));\n\n        IValue res5 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 + -10 = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testArithmeticAddDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 + 10 = 20\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(20D));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"0 + 10 = 10\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(10D));\n\n        IValue res3 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d10, d0});\n        assertThat(\"10 + 0 = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{dm10, d10});\n        assertThat(\"-10 + 10 = 0\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(0D));\n\n        IValue res5 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d10, dm10});\n        assertThat(\"10 + -10 = 0\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testArithmeticAddIntegerDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 + 10 = 20\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(20D));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, d10});\n        assertThat(\"0 + 10 = 10\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(10D));\n\n        IValue res3 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i10, d0});\n        assertThat(\"10 + 0 = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{im10, d10});\n        assertThat(\"-10 + 10 = 0\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(0D));\n\n        IValue res5 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i10, dm10});\n        assertThat(\"10 + -10 = 0\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testInvalidInputSizeAddLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeAddSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeAdd() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MINUS -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticMinusInteger() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 - 10 = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 - 10 = -10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(-10));\n\n        IValue res3 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 - 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 - 10 = -20\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-20));\n\n        IValue res5 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 - -10 = 20\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(20));\n    }\n\n    @Test\n    public void testArithmeticMinusDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 - 10 = 0\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(0D));\n\n        IValue res2 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"0 - 10 = -10\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(-10D));\n\n        IValue res3 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{d10, d0});\n        assertThat(\"10 - 0 = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{dm10, d10});\n        assertThat(\"-10 - 10 = -20\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(-20D));\n\n        IValue res5 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{d10, dm10});\n        assertThat(\"10 - -10 = 20\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(20D));\n    }\n\n    @Test\n    public void testArithmeticMinusIntegerDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 - 10 = 0\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(0D));\n\n        IValue res2 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i0, d10});\n        assertThat(\"0 - 10 = -10\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(-10D));\n\n        IValue res3 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i10, d0});\n        assertThat(\"10 - 0 = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{im10, d10});\n        assertThat(\"-10 - 10 = -20\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(-20D));\n\n        IValue res5 = Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i10, dm10});\n        assertThat(\"10 - -10 = 20\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(20D));\n    }\n\n    @Test\n    public void testInvalidInputSizeMinusLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMinusSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeMinus() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_SUBTRACTION.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MULTIPLY -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticMultiplyInteger() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 * 10 = 100\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(100));\n\n        IValue res2 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 * 0 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i10, i1});\n        assertThat(\"10 * 1 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 * 10 = 0\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(0));\n\n        IValue res5 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i1, i10});\n        assertThat(\"1 * 10 = 10\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(10));\n\n        IValue res6 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 * 10 = -100\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(-100));\n    }\n\n    @Test\n    public void testArithmeticMultiplyDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 * 10 = 100\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(100D));\n\n        IValue res2 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{d10, d0});\n        assertThat(\"10 * 0 = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{d10, d1});\n        assertThat(\"10 * 1 = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"0 * 10 = 0\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(0D));\n\n        IValue res5 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{d1, d10});\n        assertThat(\"1 * 10 = 10\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(10D));\n\n        IValue res6 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{dm10, d10});\n        assertThat(\"-10 * 10 = -100\", ((ValueTypeDouble.ValueDouble) res6).getRawValue(), is(-100D));\n    }\n\n    @Test\n    public void testArithmeticMultiplyIntegerDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 * 10 = 100\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(100D));\n\n        IValue res2 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i10, d0});\n        assertThat(\"10 * 0 = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i10, d1});\n        assertThat(\"10 * 1 = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i0, d10});\n        assertThat(\"0 * 10 = 0\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(0D));\n\n        IValue res5 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i1, d10});\n        assertThat(\"1 * 10 = 10\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(10D));\n\n        IValue res6 = Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{im10, d10});\n        assertThat(\"-10 * 10 = -100\", ((ValueTypeDouble.ValueDouble) res6).getRawValue(), is(-100D));\n    }\n\n    @Test\n    public void testInvalidInputSizeMultiplyLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMultiplySmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeMultiply() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MULTIPLICATION.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- DIVIDE -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticDivideInteger() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 / 10 = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 / 10 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 / 10 = -1\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(-1));\n\n        IValue res4 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 / -10 = -1\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-1));\n\n        IValue res5 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, i15});\n        assertThat(\"10 / 15 = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n\n        IValue res6 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i15, i10});\n        assertThat(\"15 / 10 = 1\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(1));\n    }\n\n    @Test\n    public void testArithmeticDivideDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 / 10 = 1\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(1D));\n\n        IValue res2 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"0 / 10 = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{dm10, d10});\n        assertThat(\"-10 / 10 = -1\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(-1D));\n\n        IValue res4 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{d10, dm10});\n        assertThat(\"10 / -10 = -1\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(-1D));\n\n        IValue res5 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{d10, d15});\n        assertThat(\"10 / 15 = 0.666...\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(10D / 15D));\n\n        IValue res6 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{d15, d10});\n        assertThat(\"15 / 10 = 1.5\", ((ValueTypeDouble.ValueDouble) res6).getRawValue(), is(1.5D));\n    }\n\n    @Test\n    public void testArithmeticDivideIntegerDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 / 10 = 1\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(1D));\n\n        IValue res2 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i0, d10});\n        assertThat(\"0 / 10 = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{im10, d10});\n        assertThat(\"-10 / 10 = -1\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(-1D));\n\n        IValue res4 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, dm10});\n        assertThat(\"10 / -10 = -1\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(-1D));\n\n        IValue res5 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, d15});\n        assertThat(\"10 / 15 = 0.666...\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(10D / 15D));\n\n        IValue res6 = Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i15, d10});\n        assertThat(\"15 / 10 = 1.5\", ((ValueTypeDouble.ValueDouble) res6).getRawValue(), is(1.5D));\n    }\n\n    @Test\n    public void testArithmeticDivideByZero() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i10, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeDivideLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeDivideSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeDivide() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DIVISION.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MAX -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticMaxInteger() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"max(10, 10) = 10\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(10));\n\n        IValue res2 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"max(0, 10) = 10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(10));\n\n        IValue res3 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"max(10, 0) = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i0, im10});\n        assertThat(\"max(0, -10) = 0\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(0));\n\n        IValue res5 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{im10, i0});\n        assertThat(\"max(-10, 0) = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testArithmeticMaxDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"max(10, 10) = 10\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(10D));\n\n        IValue res2 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"max(0, 10) = 10\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(10D));\n\n        IValue res3 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{d10, d0});\n        assertThat(\"max(10, 0) = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{d0, dm10});\n        assertThat(\"max(0, -10) = 0\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(0D));\n\n        IValue res5 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{dm10, d0});\n        assertThat(\"max(-10, 0) = 0\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testArithmeticMaxIntegerDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i10, d10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"max(10, 10) = 10\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(10D));\n\n        IValue res2 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i0, d10});\n        assertThat(\"max(0, 10) = 10\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(10D));\n\n        IValue res3 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i10, d0});\n        assertThat(\"max(10, 0) = 10\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10D));\n\n        IValue res4 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i0, dm10});\n        assertThat(\"max(0, -10) = 0\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(0D));\n\n        IValue res5 = Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{im10, d0});\n        assertThat(\"max(-10, 0) = 0\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testInvalidInputSizeMaxLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMaxSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeMax() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MAXIMUM.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MIN -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticMinInteger() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"min(10, 10) = 10\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(10));\n\n        IValue res2 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"min(0, 10) = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"min(10, 0) = 0\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(0));\n\n        IValue res4 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i0, im10});\n        assertThat(\"min(0, -10) = -10\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-10));\n\n        IValue res5 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{im10, i0});\n        assertThat(\"min(-10, 0) = -10\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(-10));\n    }\n\n    @Test\n    public void testArithmeticMinDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"min(10, 10) = 10\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(10D));\n\n        IValue res2 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"min(0, 10) = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{d10, d0});\n        assertThat(\"min(10, 0) = 0\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(0D));\n\n        IValue res4 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{d0, dm10});\n        assertThat(\"min(0, -10) = -10\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(-10D));\n\n        IValue res5 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{dm10, d0});\n        assertThat(\"min(-10, 0) = -10\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(-10D));\n    }\n\n    @Test\n    public void testArithmeticMinIntegerDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i10, d10});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"min(10, 10) = 10\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(10D));\n\n        IValue res2 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i0, d10});\n        assertThat(\"min(0, 10) = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i10, d0});\n        assertThat(\"min(10, 0) = 0\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(0D));\n\n        IValue res4 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i0, dm10});\n        assertThat(\"min(0, -10) = -10\", ((ValueTypeDouble.ValueDouble) res4).getRawValue(), is(-10D));\n\n        IValue res5 = Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{im10, d0});\n        assertThat(\"min(-10, 0) = -10\", ((ValueTypeDouble.ValueDouble) res5).getRawValue(), is(-10D));\n    }\n\n    @Test\n    public void testInvalidInputSizeMinLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMinSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeMin() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MINIMUM.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MODULO -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticModulo() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 % 10 = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 % 10 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 % 10 = 0\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(0));\n\n        IValue res4 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 % -10 = 0\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(0));\n\n        IValue res5 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, i15});\n        assertThat(\"10 % 15 = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(10));\n\n        IValue res6 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i15, i10});\n        assertThat(\"15 % 10 = 1\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(5));\n    }\n\n    @Test\n    public void testArithmeticModuloByIntegerOne() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, i1});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 % 1 = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i0, i1});\n        assertThat(\"0 % 1 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i1, i1});\n        assertThat(\"1 % 1 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testArithmeticModuloByDoubleOne() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, d1});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"10 % 1 = 0\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(0D));\n\n        IValue res2 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i0, d1});\n        assertThat(\"0 % 1 = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n\n        IValue res3 = Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i1, d1});\n        assertThat(\"1 % 1 = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testArithmeticModuloByIntegerZero() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, i0}));\n    }\n\n    @Test\n    public void testArithmeticModuloByDoubleZero() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i10, d0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeModuloLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeModuloSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeModulo() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_MODULUS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- INCREMENT -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticIncrement() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_INCREMENT.evaluate(new IVariable[]{i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10++ = 11\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(11));\n\n        IValue res2 = Operators.ARITHMETIC_INCREMENT.evaluate(new IVariable[]{i0});\n        assertThat(\"0++ = 1\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(1));\n\n        IValue res3 = Operators.ARITHMETIC_INCREMENT.evaluate(new IVariable[]{im10});\n        assertThat(\"-10++ = -9\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(-9));\n    }\n\n    @Test\n    public void testInvalidInputSizeIncrementLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_INCREMENT.evaluate(new IVariable[]{i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIncrementSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_INCREMENT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeIncrement() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_INCREMENT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- DECREMENT -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticDecrement() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_DECREMENT.evaluate(new IVariable[]{i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10-- = 9\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(9));\n\n        IValue res2 = Operators.ARITHMETIC_DECREMENT.evaluate(new IVariable[]{i0});\n        assertThat(\"0-- = -1\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(-1));\n\n        IValue res3 = Operators.ARITHMETIC_DECREMENT.evaluate(new IVariable[]{im10});\n        assertThat(\"-10-- = -11\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(-11));\n    }\n\n    @Test\n    public void testInvalidInputSizeDecrementLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DECREMENT.evaluate(new IVariable[]{i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeDecrementSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DECREMENT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeDecrement() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.ARITHMETIC_DECREMENT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestBinaryOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different binary operators.\n *\n * @author rubensworks\n */\npublic class TestBinaryOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger i2;\n    private DummyVariableInteger i3;\n    private DummyVariableInteger i5;\n    private DummyVariableInteger im10;\n    private DummyVariableInteger i10;\n    private DummyVariableInteger i15;\n\n    @BeforeEach\n    public void before() {\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        i2 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2));\n        i3 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(3));\n        i5 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(5));\n        im10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-10));\n        i10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10));\n        i15 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(15));\n    }\n\n    /**\n     * ----------------------------------- AND -----------------------------------\n     */\n\n    @Test\n    public void testBinaryAnd() throws EvaluationException {\n        IValue res1 = Operators.BINARY_AND.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 & 10 = 10\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(10));\n\n        IValue res2 = Operators.BINARY_AND.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 & 10 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.BINARY_AND.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 & 0 = 0\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(0));\n\n        IValue res4 = Operators.BINARY_AND.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 & 10 = 2\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(2));\n\n        IValue res5 = Operators.BINARY_AND.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 & -10 = 2\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(2));\n\n        IValue res6 = Operators.BINARY_AND.evaluate(new IVariable[]{i1, i2});\n        assertThat(\"1 & 2 = 0\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(0));\n\n        IValue res7 = Operators.BINARY_AND.evaluate(new IVariable[]{i5, i3});\n        assertThat(\"5 & 3 = 1\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(1));\n    }\n\n    @Test\n    public void testInvalidInputSizeAndLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_AND.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeAndSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_AND.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeAnd() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_AND.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- OR -----------------------------------\n     */\n\n    @Test\n    public void testBinaryOr() throws EvaluationException {\n        IValue res1 = Operators.BINARY_OR.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 | 10 = 10\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(10));\n\n        IValue res2 = Operators.BINARY_OR.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 | 10 = 10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(10));\n\n        IValue res3 = Operators.BINARY_OR.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 | 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.BINARY_OR.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 | 10 = -2\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-2));\n\n        IValue res5 = Operators.BINARY_OR.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 | -10 = -2\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(-2));\n\n        IValue res6 = Operators.BINARY_OR.evaluate(new IVariable[]{i1, i2});\n        assertThat(\"1 | 2 = 3\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(3));\n\n        IValue res7 = Operators.BINARY_OR.evaluate(new IVariable[]{i5, i3});\n        assertThat(\"5 | 3 = 7\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(7));\n    }\n\n    @Test\n    public void testInvalidInputSizeOrLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_OR.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeOrSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_OR.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeOr() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_OR.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- XOR -----------------------------------\n     */\n\n    @Test\n    public void testBinaryXor() throws EvaluationException {\n        IValue res1 = Operators.BINARY_XOR.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 ^ 10 = 10\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.BINARY_XOR.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 ^ 10 = 10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(10));\n\n        IValue res3 = Operators.BINARY_XOR.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 ^ 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.BINARY_XOR.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 ^ 10 = -4\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-4));\n\n        IValue res5 = Operators.BINARY_XOR.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 ^ -10 = -4\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(-4));\n\n        IValue res6 = Operators.BINARY_XOR.evaluate(new IVariable[]{i1, i2});\n        assertThat(\"1 ^ 2 = 3\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(3));\n\n        IValue res7 = Operators.BINARY_XOR.evaluate(new IVariable[]{i5, i3});\n        assertThat(\"5 ^ 3 = 6\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(6));\n    }\n\n    @Test\n    public void testInvalidInputSizeXorLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_XOR.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeXorSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_XOR.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeXor() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_XOR.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- COMPLEMENT -----------------------------------\n     */\n\n    @Test\n    public void testBinaryComplement() throws EvaluationException {\n        IValue res1 = Operators.BINARY_COMPLEMENT.evaluate(new IVariable[]{i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"~10 = -11\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(-11));\n\n        IValue res2 = Operators.BINARY_COMPLEMENT.evaluate(new IVariable[]{im10});\n        assertThat(\"~-10 = 9\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(9));\n    }\n\n    @Test\n    public void testInvalidInputSizeComplementLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_COMPLEMENT.evaluate(new IVariable[]{i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeComplementSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_COMPLEMENT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeComplement() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_COMPLEMENT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- LSHIFT -----------------------------------\n     */\n\n    @Test\n    public void testBinaryLShift() throws EvaluationException {\n        IValue res1 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 << 10 = 10240\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(10240));\n\n        IValue res2 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 << 10 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 << 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 << 10 = -10240\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-10240));\n\n        IValue res5 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 << -10 = 41943040\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(41943040));\n\n        IValue res6 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i1, i2});\n        assertThat(\"1 << 2 = 4\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(4));\n\n        IValue res7 = Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i5, i3});\n        assertThat(\"5 << 3 = 40\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(40));\n    }\n\n    @Test\n    public void testInvalidInputSizeLShiftLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLShiftSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_LSHIFT.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeLShift() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_LSHIFT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- RSHIFT -----------------------------------\n     */\n\n    @Test\n    public void testBinaryRShift() throws EvaluationException {\n        IValue res1 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 >> 10 = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 >> 10 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 >> 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 >> 10 = -1\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-1));\n\n        IValue res5 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 >> -10 = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n\n        IValue res6 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i1, i2});\n        assertThat(\"1 >> 2 = 0\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(0));\n\n        IValue res7 = Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i5, i3});\n        assertThat(\"5 >> 3 = 0\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputSizeRShiftLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRShiftSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_RSHIFT.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeRShift() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_RSHIFT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- RZSHIFT -----------------------------------\n     */\n\n    @Test\n    public void testBinaryRZShift() throws EvaluationException {\n        IValue res1 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"10 >>> 10 = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 >>> 10 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n\n        IValue res3 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 >>> 0 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n\n        IValue res4 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 >>> 10 = 4194303\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(4194303));\n\n        IValue res5 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 >>> -10 = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n\n        IValue res6 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i1, i2});\n        assertThat(\"1 >>> 2 = 0\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(0));\n\n        IValue res7 = Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i5, i3});\n        assertThat(\"5 >>> 3 = 0\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputSizeRZShiftLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRZShiftSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeRZShift() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.BINARY_RZSHIFT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestCurryingComplex.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.CombinedOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.CurriedOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test complex currying cases\n *\n * @author rubensworks\n */\npublic class TestCurryingComplex {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableBoolean bFalse;\n    private DummyVariableBoolean bTrue;\n\n    private DummyVariableInteger i4;\n    private DummyVariableInteger i8;\n\n    @BeforeEach\n    public void before() {\n        ValueTypeListProxyFactories.load();\n\n        bFalse = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(false));\n        bTrue = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true));\n\n        i4 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(4));\n        i8 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(8));\n    }\n\n    /**\n     * ----------------------------------- APPLY -----------------------------------\n     */\n\n    // Discovered in https://github.com/CyclopsMC/IntegratedDynamics/issues/754\n    @Test\n    public void testBind2() throws EvaluationException {\n        DummyVariableOperator oApply2 = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_APPLY_2));\n        DummyVariableOperator oFlip = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_FLIP));\n\n        IValue oApply2Flipped = Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oApply2});\n        assertThat(\"result is an operator\", oApply2Flipped, instanceOf(ValueTypeOperator.ValueOperator.class));\n        assertThat(\"result is a combined operator\", ((ValueTypeOperator.ValueOperator) oApply2Flipped).getRawValue(), instanceOf(CombinedOperator.class));\n        assertThat(\"result accepts 3 inputs\", ((ValueTypeOperator.ValueOperator) oApply2Flipped).getRawValue().getRequiredInputLength(), is(3));\n        assertThat(\"result has a correct first input\", ((ValueTypeOperator.ValueOperator) oApply2Flipped).getRawValue().getInputTypes()[0], is(ValueTypes.CATEGORY_ANY));\n        assertThat(\"result has a correct second input\", ((ValueTypeOperator.ValueOperator) oApply2Flipped).getRawValue().getInputTypes()[1], is(ValueTypes.OPERATOR));\n        assertThat(\"result has a correct third input\", ((ValueTypeOperator.ValueOperator) oApply2Flipped).getRawValue().getInputTypes()[2], is(ValueTypes.CATEGORY_ANY));\n        assertThat(\"result has a any as output\", ((ValueTypeOperator.ValueOperator) oApply2Flipped).getRawValue().getOutputType(), is(ValueTypes.CATEGORY_ANY));\n\n        IValue oBind2 = Operators.OPERATOR_PIPE.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oApply2Flipped), oFlip});\n        assertThat(\"result is an operator\", oBind2, instanceOf(ValueTypeOperator.ValueOperator.class));\n        assertThat(\"result is a combined operator\", ((ValueTypeOperator.ValueOperator) oBind2).getRawValue(), instanceOf(CombinedOperator.class));\n        assertThat(\"result accepts 1 input\", ((ValueTypeOperator.ValueOperator) oBind2).getRawValue().getRequiredInputLength(), is(1));\n        assertThat(\"result has a correct first input\", ((ValueTypeOperator.ValueOperator) oBind2).getRawValue().getInputTypes()[0], is(ValueTypes.CATEGORY_ANY));\n\n        IValue oBound4_8 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oBind2), i4, i8});\n        assertThat(\"result is an operator\", oBound4_8, instanceOf(ValueTypeOperator.ValueOperator.class));\n        assertThat(\"result is a curried operator\", ((ValueTypeOperator.ValueOperator) oBound4_8).getRawValue(), instanceOf(CurriedOperator.class));\n        assertThat(\"result accepts a single input\", ((ValueTypeOperator.ValueOperator) oBound4_8).getRawValue().getRequiredInputLength(), is(1));\n\n        DummyVariableOperator oAdd = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_ADDITION));\n\n        IValue iResult = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oBound4_8), oAdd});\n        assertThat(\"result is an integer\", iResult, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"result is 12\", ((ValueTypeInteger.ValueInteger) iResult).getRawValue(), is(12));\n    }\n\n    @Test\n    public void testBind2Overflow() throws EvaluationException {\n        // When more outputs are passed to an operator than it can handle, so they have to be passed to the next one.\n        DummyVariableOperator oApply2 = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_APPLY_2));\n        DummyVariableOperator oFlip = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_FLIP));\n\n        IValue oApply2Flipped = Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oApply2});\n\n        IValue oBind2 = Operators.OPERATOR_PIPE.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oApply2Flipped), oFlip});\n\n        IValue oBound4_8 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oBind2), i4, i8});\n\n        DummyVariableOperator oAdd = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_ADDITION));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oBound4_8), oAdd, oAdd}));\n    }\n\n    @Test\n    public void testOmegaOperator() throws EvaluationException {\n        // This is supposed to throw an EvaluationException, and not result in infinite recursion\n        DummyVariableOperator oId = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.GENERAL_IDENTITY));\n        DummyVariableOperator oApply = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_APPLY));\n\n        IValue oX = Operators.OPERATOR_PIPE2.evaluate(new IVariable[]{oId, oId, oApply});\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{new DummyVariable(ValueTypes.OPERATOR, oX), new DummyVariable(ValueTypes.OPERATOR, oX)}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestDoubleOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different integer operators.\n * @author rubensworks\n */\npublic class TestDoubleOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableDouble d16;\n    private DummyVariableDouble d2;\n    private DummyVariableDouble d3;\n\n    @BeforeEach\n    public void before() {\n        d16 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(16));\n        d2 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(2));\n        d3 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(3));\n    }\n\n    /**\n     * ----------------------------------- SQRT -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticSqrt() throws EvaluationException {\n        IValue res1 = Operators.DOUBLE_SQRT.evaluate(new IVariable[]{d16});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"sqrt(16) = 4\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(4D));\n    }\n\n    @Test\n    public void testInvalidInputSizeSqrtLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.DOUBLE_SQRT.evaluate(new IVariable[]{d16, d16}));\n    }\n    @Test\n    public void testInvalidInputSizeSqrtSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.DOUBLE_SQRT.evaluate(new IVariable[]{}));\n    }\n    @Test\n    public void testInvalidInputTypeSqrt() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.DOUBLE_SQRT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n    /**\n     * ----------------------------------- POW -----------------------------------\n     */\n\n    @Test\n    public void testArithmeticPow() throws EvaluationException {\n        IValue res1 = Operators.DOUBLE_POW.evaluate(new IVariable[]{d2, d3});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"pow(2, 3) = 8\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(8D));\n    }\n\n    @Test\n    public void testInvalidInputSizePowLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.DOUBLE_POW.evaluate(new IVariable[]{d16, d16, d16}));\n    }\n    @Test\n    public void testInvalidInputSizePowSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.DOUBLE_POW.evaluate(new IVariable[]{d16}));\n    }\n    @Test\n    public void testInvalidInputTypePow() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.DOUBLE_POW.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestGeneralOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different general operators.\n *\n * @author rubensworks\n */\npublic class TestGeneralOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableBoolean bTrue;\n    private DummyVariableBoolean bFalse;\n\n    @BeforeEach\n    public void before() {\n        bTrue = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true));\n        bFalse = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    /**\n     * ----------------------------------- CHOICE -----------------------------------\n     */\n\n    @Test\n    public void testLogicalChoice() throws EvaluationException {\n        DummyVariableInteger i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        DummyVariableInteger i2 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2));\n\n        IValue res1 = Operators.GENERAL_CHOICE.evaluate(new IVariable[]{bTrue, i1, i2});\n        assertThat(\"result is 1\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"true ? 1 : 2 = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.GENERAL_CHOICE.evaluate(new IVariable[]{bFalse, i1, i2});\n        assertThat(\"false ? 1 : 2 = 1\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(2));\n    }\n\n    @Test\n    public void testInvalidInputSizeChoiceLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_CHOICE.evaluate(new IVariable[]{bTrue, bTrue, bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeChoiceSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_CHOICE.evaluate(new IVariable[]{bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputTypeChoice() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_CHOICE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testInvalidLogicalChoiceDifferentTypes() throws EvaluationException {\n        DummyVariableInteger i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_CHOICE.evaluate(new IVariable[]{bFalse, i1, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- IDENTITY -----------------------------------\n     */\n\n    @Test\n    public void testLogicalIdentity() throws EvaluationException {\n        DummyVariableInteger i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n\n        IValue res1 = Operators.GENERAL_IDENTITY.evaluate(new IVariable[]{i1});\n        assertThat(\"result is 1\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"1 = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.GENERAL_IDENTITY.evaluate(new IVariable[]{bFalse});\n        assertThat(\"false = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeIdentityLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_IDENTITY.evaluate(new IVariable[]{bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIdentitySmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_IDENTITY.evaluate(new IVariable[]{}));\n    }\n\n    /**\n     * ----------------------------------- CONSTANT -----------------------------------\n     */\n\n    @Test\n    public void testLogicalConstant() throws EvaluationException {\n        DummyVariableInteger i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n\n        IValue res1 = Operators.GENERAL_CONSTANT.evaluate(new IVariable[]{i1, bFalse});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"K 1 false = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.GENERAL_CONSTANT.evaluate(new IVariable[]{bFalse, i1});\n        assertThat(\"result is a boolean\", res2, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"K false 1 = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeConstantLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_CONSTANT.evaluate(new IVariable[]{bTrue, bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeConstantSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.GENERAL_CONSTANT.evaluate(new IVariable[]{bTrue}));\n    }\n\n    /**\n     * ----------------------------------- ISNULL -----------------------------------\n     */\n\n    @Test\n    public void testLogicalIsNull() throws EvaluationException {\n        DummyVariableInteger i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n\n        IValue res1 = Operators.NULLABLE_ISNULL.evaluate(new IVariable[]{i1});\n        assertThat(\"result is false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.NULLABLE_ISNULL.evaluate(new IVariable[]{bFalse});\n        assertThat(\"result is false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeIsNullLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NULLABLE_ISNULL.evaluate(new IVariable[]{bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIsNullSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NULLABLE_ISNULL.evaluate(new IVariable[]{}));\n    }\n\n    /**\n     * ----------------------------------- ISNOTNULL -----------------------------------\n     */\n\n    @Test\n    public void testLogicalIsNotNull() throws EvaluationException {\n        DummyVariableInteger i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n\n        IValue res1 = Operators.NULLABLE_ISNOTNULL.evaluate(new IVariable[]{i1});\n        assertThat(\"result is true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.NULLABLE_ISNOTNULL.evaluate(new IVariable[]{bFalse});\n        assertThat(\"result is true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeIsNotNullLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NULLABLE_ISNOTNULL.evaluate(new IVariable[]{bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIsNotNullSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NULLABLE_ISNOTNULL.evaluate(new IVariable[]{}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestImplicitArithmeticCasting.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.not;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test implicit casting in arithmetic operators.\n * Expects the conversions Integer -> Long -> Double, i.e. simplest int type to most complex float type.\n * @author met4000\n */\npublic class TestImplicitArithmeticCasting {\n\n    static { CyclopsCoreInstance.MOD = new ModBaseMocked(); }\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableInteger i0;\n    private DummyVariableLong l0;\n    private DummyVariableDouble d0;\n\n    @BeforeEach\n    public void before() {\n        ValueCastMappings.load();\n\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        l0 = new DummyVariableLong(ValueTypeLong.ValueLong.of(0));\n        d0 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0));\n    }\n\n    /**\n     * ----------------------------------- SAME TYPE -----------------------------------\n     */\n\n    /**\n     * Verifies that an arithmetic operation with two inputs of the same type has the same output type.\n     * Uses {@link Operators#ARITHMETIC_ADDITION} for testing - it is assumed that all other operations will behave the same.\n     * Any operators that behave differently need separate tests.\n     * @throws EvaluationException\n     */\n    @Test\n    public void testImplicitArithmeticCastSameType() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, i0});\n        assertThat(\"i0 + i0 = i0\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{l0, l0});\n        assertThat(\"l0 + l0 = l0\", res2, instanceOf(ValueTypeLong.ValueLong.class));\n\n        IValue res3 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d0, d0});\n        assertThat(\"d0 + d0 = d0\", res3, instanceOf(ValueTypeDouble.ValueDouble.class));\n    }\n\n    /**\n     * Fails if all type checks incorrectly always return true.\n     * @throws EvaluationException\n     */\n    @Test\n    public void testImplicitArithmeticCastSameTypeFalseNegative() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, i0});\n        assertThat(\"i0 + i0 != l0\", res1, not(instanceOf(ValueTypeLong.ValueLong.class)));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{l0, l0});\n        assertThat(\"l0 + l0 != d0\", res2, not(instanceOf(ValueTypeDouble.ValueDouble.class)));\n\n        IValue res3 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d0, d0});\n        assertThat(\"d0 + d0 != i0\", res3, not(instanceOf(ValueTypeInteger.ValueInteger.class)));\n    }\n\n    /**\n     * ----------------------------------- TO INTEGER -----------------------------------\n     */\n\n    // N/A\n\n    /**\n     * ----------------------------------- TO LONG -----------------------------------\n     */\n\n    @Test\n    public void testImplicitArithmeticCastIntToLong() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, l0});\n        assertThat(\"i0 + l0 = l0\", res1, instanceOf(ValueTypeLong.ValueLong.class));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{l0, i0});\n        assertThat(\"l0 + i0 = l0\", res2, instanceOf(ValueTypeLong.ValueLong.class));\n    }\n\n    /**\n     * ----------------------------------- TO DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testImplicitArithmeticCastIntToDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{i0, d0});\n        assertThat(\"i0 + d0 = d0\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d0, i0});\n        assertThat(\"d0 + i0 = d0\", res2, instanceOf(ValueTypeDouble.ValueDouble.class));\n    }\n\n    @Test\n    public void testImplicitArithmeticCastLongToDouble() throws EvaluationException {\n        IValue res1 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{l0, d0});\n        assertThat(\"l0 + d0 = d0\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n\n        IValue res2 = Operators.ARITHMETIC_ADDITION.evaluate(new IVariable[]{d0, l0});\n        assertThat(\"d0 + l0 = d0\", res2, instanceOf(ValueTypeDouble.ValueDouble.class));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestIntegerOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.junit.jupiter.api.BeforeEach;\n\n\n/**\n * Test the different integer operators.\n * @author rubensworks\n */\npublic class TestIntegerOperators {\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger im10;\n    private DummyVariableInteger i10;\n    private DummyVariableInteger i15;\n\n    @BeforeEach\n    public void before() {\n        i0   = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0  ));\n        i1   = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1  ));\n        im10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-10));\n        i10  = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10 ));\n        i15  = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(15 ));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestListOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different integer operators.\n *\n * @author rubensworks\n */\npublic class TestListOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableList labc;\n    private DummyVariableList lintegers;\n    private DummyVariableList lintegers_012;\n    private DummyVariableList lempty;\n    private DummyVariableList lintegers_dup;\n    private DummyVariableList lintegers_rev_dup;\n    private DummyVariableList llongs_hash_collision;\n    private DummyVariableList lintegers_inf;\n\n    private DummyVariableInteger im1;\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger i2;\n    private DummyVariableInteger i3;\n    private DummyVariableInteger i4;\n    private DummyVariableInteger i5;\n\n    private DummyVariableString sx;\n\n    private DummyVariableOperator oRelationalEquals;\n    private DummyVariableOperator oArithmeticIncrement;\n\n    @BeforeAll\n    public static void beforeClass() {\n        ValueTypeListProxyFactories.load();\n    }\n\n    @BeforeEach\n    public void before() {\n        im1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-1));\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        i2 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2));\n        i3 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(3));\n        i4 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(4));\n        i5 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(5));\n\n        sx = new DummyVariableString(ValueTypeString.ValueString.of(\"x\"));\n\n        oRelationalEquals = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.RELATIONAL_EQUALS));\n        oArithmeticIncrement = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_INCREMENT));\n\n        labc = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeString.ValueString.of(\"a\"),\n                ValueTypeString.ValueString.of(\"b\"),\n                ValueTypeString.ValueString.of(\"c\")\n        ));\n        lintegers = new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue(), i3.getValue()));\n        lintegers_012 = new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue()));\n        lempty = new DummyVariableList(ValueTypeList.ValueList.ofAll());\n        lintegers_dup = new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue(),\n                i3.getValue(), i1.getValue(), i2.getValue(), i3.getValue(), i2.getValue(), i3.getValue(), i3.getValue()));\n        lintegers_rev_dup = new DummyVariableList(ValueTypeList.ValueList.ofAll(i3.getValue(), i2.getValue(), i1.getValue(),\n                i0.getValue(), i2.getValue(), i1.getValue(), i0.getValue(), i1.getValue(), i0.getValue(), i0.getValue()));\n        llongs_hash_collision = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeLong.ValueLong.of(0xAAAAAAAA12345678L),\n                ValueTypeLong.ValueLong.of(0x3333333312345678L),\n                ValueTypeLong.ValueLong.of(0x12345678AAAAAAAAL),\n                ValueTypeLong.ValueLong.of(0x1234567833333333L)\n        ));\n        lintegers_inf = new DummyVariableList(ValueTypeList.ValueList.ofFactory(new ValueTypeListProxyLazyBuilt<>(\n                ValueTypeInteger.ValueInteger.of(0), Operators.ARITHMETIC_INCREMENT)));\n    }\n\n    /**\n     * ----------------------------------- LENGTH -----------------------------------\n     */\n\n    @Test\n    public void testListLength() throws EvaluationException {\n        IValue res1 = Operators.LIST_LENGTH.evaluate(new IVariable[]{labc});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"len(abc) = 3\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(3));\n    }\n\n    @Test\n    public void testInvalidInputSizeLengthLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LENGTH.evaluate(new IVariable[]{labc, labc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLengthSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LENGTH.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeLength() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LENGTH.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- EMPTY -----------------------------------\n     */\n\n    @Test\n    public void testListEmpty() throws EvaluationException {\n        IValue res1 = Operators.LIST_EMPTY.evaluate(new IVariable[]{labc});\n        assertThat(\"result is an boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"empty(abc) = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.LIST_EMPTY.evaluate(new IVariable[]{lempty});\n        assertThat(\"empty(empty) = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeEmptyLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EMPTY.evaluate(new IVariable[]{labc, labc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEmptySmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EMPTY.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeEmpty() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EMPTY.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- NOT_EMPTY -----------------------------------\n     */\n\n    @Test\n    public void testListNotEmpty() throws EvaluationException {\n        IValue res1 = Operators.LIST_NOT_EMPTY.evaluate(new IVariable[]{labc});\n        assertThat(\"result is an boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"empty(abc) = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.LIST_NOT_EMPTY.evaluate(new IVariable[]{lempty});\n        assertThat(\"empty(empty) = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeNotEmptyLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_NOT_EMPTY.evaluate(new IVariable[]{labc, labc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeNotEmptySmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_NOT_EMPTY.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNotEmpty() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_NOT_EMPTY.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- GET -----------------------------------\n     */\n\n    @Test\n    public void testListElement() throws EvaluationException {\n        IValue res1 = Operators.LIST_ELEMENT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0))});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"get(abc, 0) = 'a'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"a\"));\n\n        IValue res2 = Operators.LIST_ELEMENT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1))});\n        assertThat(\"result is a string\", res2, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"get(abc, 1) = 'b'\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"b\"));\n\n        IValue res3 = Operators.LIST_ELEMENT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2))});\n        assertThat(\"result is a string\", res3, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"get(abc, 2) = 'c'\", ((ValueTypeString.ValueString) res3).getRawValue(), is(\"c\"));\n    }\n\n    @Test\n    public void testListElementIndexOutOfBounds() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(3))}));\n    }\n\n    @Test\n    public void testInvalidInputSizeElementLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT.evaluate(new IVariable[]{labc, labc, labc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeElementSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT.evaluate(new IVariable[]{labc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeElement() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- GET_OR_DEFAULT -----------------------------------\n     */\n\n    @Test\n    public void testListElementOrDefault() throws EvaluationException {\n        IValue res1 = Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0)), sx});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"getOrDefault(abc, x, 0) = 'a'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"a\"));\n\n        IValue res2 = Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1)), sx});\n        assertThat(\"result is a string\", res2, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"getOrDefault(abc, x, 1) = 'b'\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"b\"));\n\n        IValue res3 = Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2)), sx});\n        assertThat(\"result is a string\", res3, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"getOrDefault(abc, x, 2) = 'c'\", ((ValueTypeString.ValueString) res3).getRawValue(), is(\"c\"));\n    }\n\n    @Test\n    public void testListElementOrDefaultIndexOutOfBounds() throws EvaluationException {\n        IValue res1 = Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(3)), sx});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"getOrDefault(abc, x, 3) = 'x'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"x\"));\n\n        IValue res2 = Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-1)), sx});\n        assertThat(\"getOrDefault(abc, x, -1) = 'x'\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"x\"));\n    }\n\n    @Test\n    public void testInvalidInputSizeElementOrDefaultLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, i0, sx, sx}));\n    }\n\n    @Test\n    public void testInvalidInputSizeElementOrDefaultSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{labc, i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeElementOrDefault() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_ELEMENT_DEFAULT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CONTAINS -----------------------------------\n     */\n\n    @Test\n    public void testListContains() throws EvaluationException {\n        IValue res1 = Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers, i0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"contains([0, 1, 2, 3], 0) = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers, i1});\n        assertThat(\"contains([0, 1, 2, 3, 1) = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers, i2});\n        assertThat(\"contains([0, 1, 2, 3], 2) = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers, i3});\n        assertThat(\"contains([0, 1, 2, 3], 3) = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n\n        IValue res5 = Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers, i4});\n        assertThat(\"contains([0, 1, 2, 3], 4) = false\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers, i2, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONTAINS.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeContains() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONTAINS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CONTAINS_PREDICATE -----------------------------------\n     */\n\n    @Test\n    public void testListContainsPredicate() throws EvaluationException {\n        DummyVariableOperator equals0 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i0}));\n        DummyVariableOperator equals1 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i1}));\n        DummyVariableOperator equals2 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n        DummyVariableOperator equals3 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i3}));\n        DummyVariableOperator equals4 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i4}));\n\n        IValue res1 = Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers, equals0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"containspredicate([0, 1, 2, 3], 0==) = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers, equals1});\n        assertThat(\"containspredicate([0, 1, 2, 3], 1==) = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers, equals2});\n        assertThat(\"containspredicate([0, 1, 2, 3], 2==) = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers, equals3});\n        assertThat(\"containspredicate([0, 1, 2, 3], 3==) = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n\n        IValue res5 = Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers, equals4});\n        assertThat(\"containspredicate([0, 1, 2, 3], 4==) = false\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsPredicateLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers, oRelationalEquals, i2}));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsPredicateSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeContainsPredicate() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONTAINS_PREDICATE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- COUNT -----------------------------------\n     */\n\n    @Test\n    public void testListCount() throws EvaluationException {\n        IValue res1 = Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers_dup, i0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"count([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 0) = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers_dup, i1});\n        assertThat(\"count([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 1) = 2\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(2));\n\n        IValue res3 = Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers_dup, i2});\n        assertThat(\"count([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 2) = 3\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(3));\n\n        IValue res4 = Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers_dup, i3});\n        assertThat(\"count([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 3) = 4\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(4));\n\n        IValue res5 = Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers_dup, i4});\n        assertThat(\"count([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 4) = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputSizeCountLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers, i2, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeCountSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeCount() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testListCountInfinite() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT.evaluate(new IVariable[]{lintegers_inf, i0}));\n    }\n\n    /**\n     * ----------------------------------- COUNT_PREDICATE -----------------------------------\n     */\n\n    @Test\n    public void testListCountPredicate() throws EvaluationException {\n        DummyVariableOperator equals0 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i0}));\n        DummyVariableOperator equals1 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i1}));\n        DummyVariableOperator equals2 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n        DummyVariableOperator equals3 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i3}));\n        DummyVariableOperator equals4 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i4}));\n\n        IValue res1 = Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers_dup, equals0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"countpredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 0) = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers_dup, equals1});\n        assertThat(\"countpredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 1) = 2\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(2));\n\n        IValue res3 = Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers_dup, equals2});\n        assertThat(\"countpredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 2) = 3\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(3));\n\n        IValue res4 = Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers_dup, equals3});\n        assertThat(\"countpredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 3) = 4\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(4));\n\n        IValue res5 = Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers_dup, equals4});\n        assertThat(\"countpredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], 4) = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputSizeCountPredicateLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers, i2, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeCountPredicateSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeCountPredicate() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testListCountPredicateInfinite() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_COUNT_PREDICATE.evaluate(new IVariable[]{lintegers_inf, i0}));\n    }\n\n    /**\n     * ----------------------------------- APPEND -----------------------------------\n     */\n\n    @Test\n    public void testListAppend() throws EvaluationException {\n        IValue res1 = Operators.LIST_APPEND.evaluate(new IVariable[]{lintegers_012, i3});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"append([0, 1, 2], 3)[0] = 0\", list.get(0).getRawValue(), is(0));\n        assertThat(\"append([0, 1, 2], 3)[1] = 1\", list.get(1).getRawValue(), is(1));\n        assertThat(\"append([0, 1, 2], 3)[2] = 2\", list.get(2).getRawValue(), is(2));\n        assertThat(\"append([0, 1, 2], 3)[3] = 3\", list.get(3).getRawValue(), is(3));\n        assertThat(\"append([0, 1, 2], 3).size = 3\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testInvalidInputSizeAppendInvalidType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_APPEND.evaluate(new IVariable[]{lintegers_012, oRelationalEquals}));\n    }\n\n    @Test\n    public void testInvalidInputSizeAppendLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_APPEND.evaluate(new IVariable[]{lintegers, i2, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeAppendSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_APPEND.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeAppend() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_APPEND.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CONCAT -----------------------------------\n     */\n\n    @Test\n    public void testListConcat() throws EvaluationException {\n        IValue res1 = Operators.LIST_CONCAT.evaluate(new IVariable[]{lintegers_012, lintegers});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[0] = 0\", list.get(0).getRawValue(), is(0));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[1] = 1\", list.get(1).getRawValue(), is(1));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[2] = 2\", list.get(2).getRawValue(), is(2));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[3] = 0\", list.get(3).getRawValue(), is(0));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[4] = 1\", list.get(4).getRawValue(), is(1));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[5] = 2\", list.get(5).getRawValue(), is(2));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3])[6] = 3\", list.get(6).getRawValue(), is(3));\n        assertThat(\"concat([0, 1, 2], [0, 1, 2, 3]).size = 7\", list.getLength(), is(7));\n    }\n\n    @Test\n    public void testInvalidInputSizeConcatInvalidType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONCAT.evaluate(new IVariable[]{lintegers_012, oRelationalEquals}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONCAT.evaluate(new IVariable[]{oRelationalEquals, lintegers_012}));\n    }\n\n    @Test\n    public void testInvalidInputSizeConcatLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONCAT.evaluate(new IVariable[]{lintegers, lintegers_012, lintegers_012}));\n    }\n\n    @Test\n    public void testInvalidInputSizeConcatSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONCAT.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeConcat() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_CONCAT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- LAZYBUILT -----------------------------------\n     */\n\n    @Test\n    public void testListLazyBuilt() throws EvaluationException {\n        IValue res1 = Operators.LIST_LAZYBUILT.evaluate(new IVariable[]{i3, oArithmeticIncrement});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"lazybuilt(3, ++)[0] = 3\", list.get(0).getRawValue(), is(3));\n        assertThat(\"lazybuilt(3, ++)[1] = 4\", list.get(1).getRawValue(), is(4));\n        assertThat(\"lazybuilt(3, ++)[5] = 8\", list.get(5).getRawValue(), is(8));\n        assertThat(\"lazybuilt(3, ++)[10] = 13\", list.get(10).getRawValue(), is(13));\n        assertThat(\"lazybuilt(3, ++)[100] = 103\", list.get(100).getRawValue(), is(103));\n\n        assertThat(\"lazybuilt([0, 1, 2], 3).size = 3\", list.getLength(), is(Integer.MAX_VALUE));\n    }\n\n    @Test\n    public void testInvalidInputSizeLazyBuiltInvalidType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LAZYBUILT.evaluate(new IVariable[]{i3, oRelationalEquals}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLazyBuiltLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LAZYBUILT.evaluate(new IVariable[]{lintegers, i2, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLazyBuiltSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LAZYBUILT.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeLazyBuilt() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_LAZYBUILT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- HEAD -----------------------------------\n     */\n\n    @Test\n    public void testListHead() throws EvaluationException {\n        IValue res1 = Operators.LIST_HEAD.evaluate(new IVariable[]{labc});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"head(abc) = 'a'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"a\"));\n    }\n\n    @Test\n    public void testInvalidInputSizeHeadLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_HEAD.evaluate(new IVariable[]{labc, labc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeHeadSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_HEAD.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeHead() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_HEAD.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- TAIL -----------------------------------\n     */\n\n    @Test\n    public void testListTail() throws EvaluationException {\n        IValue res1 = Operators.LIST_TAIL.evaluate(new IVariable[]{lintegers});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"tail([0, 1, 2, 3])[0] = 0\", list.get(0).getRawValue(), is(1));\n        assertThat(\"tail([0, 1, 2, 3])[1] = 1\", list.get(1).getRawValue(), is(2));\n        assertThat(\"tail([0, 1, 2, 3])[2] = 2\", list.get(2).getRawValue(), is(3));\n        assertThat(\"tail([0, 1, 2, 3]).size = 3\", list.getLength(), is(3));\n    }\n\n    @Test\n    public void testInvalidInputSizeTailLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_TAIL.evaluate(new IVariable[]{lintegers, i2}));\n    }\n\n    @Test\n    public void testInvalidInputSizeTailSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_TAIL.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeTail() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_TAIL.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- UNIQ_PREDICATE -----------------------------------\n     */\n\n    @Test\n    public void testListUniqPredicate() throws EvaluationException {\n        IValue res1 = Operators.LIST_UNIQ_PREDICATE.evaluate(new IVariable[]{lintegers_dup, oRelationalEquals});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"uniqPredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], ==)[0] = 0\", list.get(0).getRawValue(), is(0));\n        assertThat(\"uniqPredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], ==)[1] = 1\", list.get(1).getRawValue(), is(1));\n        assertThat(\"uniqPredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], ==)[2] = 2\", list.get(2).getRawValue(), is(2));\n        assertThat(\"uniqPredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], ==)[3] = 3\", list.get(3).getRawValue(), is(3));\n        assertThat(\"uniqPredicate([0, 1, 2, 3, 1, 2, 3, 2, 3, 3], ==).size = 4\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testListUniqPredicateOrder() throws EvaluationException {\n        IValue res1 = Operators.LIST_UNIQ_PREDICATE.evaluate(new IVariable[]{lintegers_rev_dup, oRelationalEquals});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"uniqPredicate([3, 2, 1, 0, 2, 1, 0, 1, 0, 0], ==)[0] = 3\", list.get(0).getRawValue(), is(3));\n        assertThat(\"uniqPredicate([3, 2, 1, 0, 2, 1, 0, 1, 0, 0], ==)[1] = 2\", list.get(1).getRawValue(), is(2));\n        assertThat(\"uniqPredicate([3, 2, 1, 0, 2, 1, 0, 1, 0, 0], ==)[2] = 1\", list.get(2).getRawValue(), is(1));\n        assertThat(\"uniqPredicate([3, 2, 1, 0, 2, 1, 0, 1, 0, 0], ==)[3] = 0\", list.get(3).getRawValue(), is(0));\n        assertThat(\"uniqPredicate([3, 2, 1, 0, 2, 1, 0, 1, 0, 0], ==).size = 4\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testListUniqPredicateHashCollision() throws EvaluationException {\n        IValue res1 = Operators.LIST_UNIQ_PREDICATE.evaluate(new IVariable[]{llongs_hash_collision, oRelationalEquals});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeLong, ValueTypeLong.ValueLong> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"uniqPredicate([0xAAAAAAAA12345678L, 0x3333333312345678L, 0x12345678AAAAAAAAL, 0x1234567833333333L], ==).size = 4\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testInvalidInputSizeUniqPredicateLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_UNIQ_PREDICATE.evaluate(new IVariable[]{lintegers, oRelationalEquals, i2}));\n    }\n\n    @Test\n    public void testInvalidInputSizeUniqPredicateSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_UNIQ_PREDICATE.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeUniqPredicate() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_UNIQ_PREDICATE.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- UNIQ -----------------------------------\n     */\n\n    @Test\n    public void testListUniq() throws EvaluationException {\n        IValue res1 = Operators.LIST_UNIQ.evaluate(new IVariable[]{lintegers_dup});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"uniq([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])[0] = 0\", list.get(0).getRawValue(), is(0));\n        assertThat(\"uniq([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])[1] = 1\", list.get(1).getRawValue(), is(1));\n        assertThat(\"uniq([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])[2] = 2\", list.get(2).getRawValue(), is(2));\n        assertThat(\"uniq([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])[3] = 3\", list.get(3).getRawValue(), is(3));\n        assertThat(\"uniq([0, 1, 2, 3, 1, 2, 3, 2, 3, 3]).size = 4\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testListUniqOrder() throws EvaluationException {\n        IValue res1 = Operators.LIST_UNIQ.evaluate(new IVariable[]{lintegers_rev_dup});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"uniq([3, 2, 1, 0, 2, 1, 0, 1, 0, 0])[0] = 3\", list.get(0).getRawValue(), is(3));\n        assertThat(\"uniq([3, 2, 1, 0, 2, 1, 0, 1, 0, 0])[1] = 2\", list.get(1).getRawValue(), is(2));\n        assertThat(\"uniq([3, 2, 1, 0, 2, 1, 0, 1, 0, 0])[2] = 1\", list.get(2).getRawValue(), is(1));\n        assertThat(\"uniq([3, 2, 1, 0, 2, 1, 0, 1, 0, 0])[3] = 0\", list.get(3).getRawValue(), is(0));\n        assertThat(\"uniq([3, 2, 1, 0, 2, 1, 0, 1, 0, 0]).size = 4\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testListUniqHashCollision() throws EvaluationException {\n        IValue res1 = Operators.LIST_UNIQ.evaluate(new IVariable[]{llongs_hash_collision});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeLong, ValueTypeLong.ValueLong> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"uniq([0xAAAAAAAA12345678L, 0x3333333312345678L, 0x12345678AAAAAAAAL, 0x1234567833333333L]).size = 4\", list.getLength(), is(4));\n    }\n\n    @Test\n    public void testInvalidInputSizeUniqLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_UNIQ.evaluate(new IVariable[]{lintegers, i2}));\n    }\n\n    @Test\n    public void testInvalidInputSizeUniqSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_UNIQ.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeUniq() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_UNIQ.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- SLICE -----------------------------------\n     */\n\n    @Test\n    public void testListSlice() throws EvaluationException {\n        IValue res1 = Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i0, i4});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list = ((ValueTypeList.ValueList) res1).getRawValue();\n\n        assertThat(\"slice([0, 1, 2, 3], 0, 4)[0] = 0\", list.get(0).getRawValue(), is(0));\n        assertThat(\"slice([0, 1, 2, 3], 0, 4)[1] = 1\", list.get(1).getRawValue(), is(1));\n        assertThat(\"slice([0, 1, 2, 3], 0, 4)[2] = 2\", list.get(2).getRawValue(), is(2));\n        assertThat(\"slice([0, 1, 2, 3], 0, 4)[3] = 3\", list.get(3).getRawValue(), is(3));\n        assertThat(\"slice([0, 1, 2, 3], 0, 4).size = 4\", list.getLength(), is(4));\n\n        IValue res2 = Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i1, i4});\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list2 = ((ValueTypeList.ValueList) res2).getRawValue();\n\n        assertThat(\"slice([0, 1, 2, 3], 1, 4)[0] = 1\", list2.get(0).getRawValue(), is(1));\n        assertThat(\"slice([0, 1, 2, 3], 1, 4)[1] = 2\", list2.get(1).getRawValue(), is(2));\n        assertThat(\"slice([0, 1, 2, 3], 1, 4)[2] = 3\", list2.get(2).getRawValue(), is(3));\n        assertThat(\"slice([0, 1, 2, 3], 1, 4).size = 3\", list2.getLength(), is(3));\n\n        IValue res3 = Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i3, i5});\n        IValueTypeListProxy<ValueTypeInteger, ValueTypeInteger.ValueInteger> list3 = ((ValueTypeList.ValueList) res3).getRawValue();\n\n        assertThat(\"slice([0, 1, 2, 3], 3, 5)[0] = 3\", list3.get(0).getRawValue(), is(3));\n        assertThat(\"slice([0, 1, 2, 3], 3, 5).size = 1\", list3.getLength(), is(1));\n    }\n\n    @Test\n    public void testInvalidInputSizeSliceNegative1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i0, im1}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSliceNegative2() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, im1, i1}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSliceToNotLargerThanFrom() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i1, i1}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSliceLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i2, i2, i2}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSliceSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_SLICE.evaluate(new IVariable[]{lintegers, i2}));\n    }\n\n    @Test\n    public void testInvalidInputTypeSlice() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_SLICE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- INTERSECTION -----------------------------------\n     */\n\n    @Test\n    public void testIntersection() throws EvaluationException {\n        DummyVariableList list1 = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeString.ValueString.of(\"a\"),\n                ValueTypeString.ValueString.of(\"b\"),\n                ValueTypeString.ValueString.of(\"c\")\n        ));\n        DummyVariableList list2 = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeString.ValueString.of(\"d\"),\n                ValueTypeString.ValueString.of(\"c\"),\n                ValueTypeString.ValueString.of(\"b\")\n        ));\n        IValue result = Operators.LIST_INTERSECTION.evaluate(new IVariable[]{list1, list2});\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> resultValues = ((ValueTypeList.ValueList) result).getRawValue();\n        assertThat(\n                \"len(a,b,c ∩ d,c,b) == 2\",\n                resultValues.getLength(),\n                is(2)\n        );\n        assertThat(\n                \"(a,b,c ∩ d,c,b)[0]\",\n                resultValues.get(0).getRawValue(),\n                is(\"b\")\n        );\n        assertThat(\n                \"(a,b,c ∩ d,c,b)[1]\",\n                resultValues.get(1).getRawValue(),\n                is(\"c\")\n        );\n    }\n\n    /**\n     * ----------------------------------- EQUALS_SET -----------------------------------\n     */\n\n    @Test\n    public void testListEqualsSet() throws EvaluationException {\n        IValue res1 = Operators.LIST_EQUALS_SET.evaluate(new IVariable[]{\n                new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i2.getValue(), i3.getValue(), i3.getValue(), i1.getValue())),\n                new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue(), i3.getValue()))\n        });\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"result is true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsSetInvalidType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_SET.evaluate(new IVariable[]{lintegers_012, oRelationalEquals}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_SET.evaluate(new IVariable[]{oRelationalEquals, lintegers_012}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsSetLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_SET.evaluate(new IVariable[]{lintegers, lintegers_012, lintegers_012}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsSetSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_SET.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeEqualsSet() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_SET.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- EQUALS_MULTISET -----------------------------------\n     */\n\n    @Test\n    public void testListEqualsMultiSet() throws EvaluationException {\n        IValue res1 = Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{\n                new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i2.getValue(), i3.getValue(), i3.getValue(), i1.getValue())),\n                new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue(), i3.getValue()))\n        });\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"result is true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{\n                new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i2.getValue(), i3.getValue(), i3.getValue(), i1.getValue())),\n                new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue(), i3.getValue(), i3.getValue()))\n        });\n        assertThat(\"result is true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsMultiSetInvalidType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{lintegers_012, oRelationalEquals}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{oRelationalEquals, lintegers_012}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsMultiSetLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{lintegers, lintegers_012, lintegers_012}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsMultiSetSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputTypeEqualsMultiSet() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LIST_EQUALS_MULTISET.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestLogicalOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different logical operators.\n *\n * @author rubensworks\n */\npublic class TestLogicalOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableBoolean bTrue;\n    private DummyVariableBoolean bFalse;\n\n    @BeforeEach\n    public void before() {\n        bTrue = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true));\n        bFalse = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(false));\n    }\n\n    /**\n     * ----------------------------------- AND -----------------------------------\n     */\n\n    @Test\n    public void testLogicalAnd() throws EvaluationException {\n        IValue res1 = Operators.LOGICAL_AND.evaluate(new IVariable[]{bTrue, bTrue});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class)); // We can't use hamcrest's isA because of: https://github.com/hamcrest/JavaHamcrest/issues/27\n        assertThat(\"true && true = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.LOGICAL_AND.evaluate(new IVariable[]{bTrue, bFalse});\n        assertThat(\"true && false = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.LOGICAL_AND.evaluate(new IVariable[]{bFalse, bTrue});\n        assertThat(\"false && true = false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.LOGICAL_AND.evaluate(new IVariable[]{bFalse, bFalse});\n        assertThat(\"false && false = false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeAndLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_AND.evaluate(new IVariable[]{bTrue, bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeAndSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_AND.evaluate(new IVariable[]{bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputTypeAnd() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_AND.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testShortCircuitingAnd() throws EvaluationException {\n        Operators.LOGICAL_AND.evaluate(new IVariable[]{bFalse, bTrue});\n        assertThat(\"first variable was called\", bFalse.isFetched(), is(true));\n        assertThat(\"second variable was not called\", bTrue.isFetched(), is(false));\n    }\n\n    /**\n     * ----------------------------------- OR -----------------------------------\n     */\n\n    @Test\n    public void testLogicalOr() throws EvaluationException {\n        IValue res1 = Operators.LOGICAL_OR.evaluate(new IVariable[]{bTrue, bTrue});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class)); // We can't use hamcrest's isA because of: https://github.com/hamcrest/JavaHamcrest/issues/27\n        assertThat(\"true || true = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.LOGICAL_OR.evaluate(new IVariable[]{bTrue, bFalse});\n        assertThat(\"true || false = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.LOGICAL_OR.evaluate(new IVariable[]{bFalse, bTrue});\n        assertThat(\"false || true = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.LOGICAL_OR.evaluate(new IVariable[]{bFalse, bFalse});\n        assertThat(\"false || false = false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeOrLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_OR.evaluate(new IVariable[]{bTrue, bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeOrSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_OR.evaluate(new IVariable[]{bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputTypeOr() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_OR.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testShortCircuitingOr() throws EvaluationException {\n        Operators.LOGICAL_OR.evaluate(new IVariable[]{bTrue, bFalse});\n        assertThat(\"first variable was called\", bTrue.isFetched(), is(true));\n        assertThat(\"second variable was not called\", bFalse.isFetched(), is(false));\n    }\n\n    /**\n     * ----------------------------------- NOT -----------------------------------\n     */\n\n    @Test\n    public void testLogicalNot() throws EvaluationException {\n        IValue res1 = Operators.LOGICAL_NOT.evaluate(new IVariable[]{bTrue});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class)); // We can't use hamcrest's isA because of: https://github.com/hamcrest/JavaHamcrest/issues/27\n        assertThat(\"!true = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.LOGICAL_NOT.evaluate(new IVariable[]{bFalse});\n        assertThat(\"!false = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeNotLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NOT.evaluate(new IVariable[]{bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeNotSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NOT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNot() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NOT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- NAND -----------------------------------\n     */\n\n    @Test\n    public void testLogicalNand() throws EvaluationException {\n        IValue res1 = Operators.LOGICAL_NAND.evaluate(new IVariable[]{bTrue, bTrue});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class)); // We can't use hamcrest's isA because of: https://github.com/hamcrest/JavaHamcrest/issues/27\n        assertThat(\"true !&& true = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.LOGICAL_NAND.evaluate(new IVariable[]{bTrue, bFalse});\n        assertThat(\"true !&& false = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.LOGICAL_NAND.evaluate(new IVariable[]{bFalse, bTrue});\n        assertThat(\"false !&& true = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.LOGICAL_NAND.evaluate(new IVariable[]{bFalse, bFalse});\n        assertThat(\"false !&& false = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeNandLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NAND.evaluate(new IVariable[]{bTrue, bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeNandSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NAND.evaluate(new IVariable[]{bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNand() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NAND.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testShortCircuitingNand() throws EvaluationException {\n        Operators.LOGICAL_NAND.evaluate(new IVariable[]{bFalse, bTrue});\n        assertThat(\"first variable was called\", bFalse.isFetched(), is(true));\n        assertThat(\"second variable was not called\", bTrue.isFetched(), is(false));\n    }\n\n    /**\n     * ----------------------------------- NOR -----------------------------------\n     */\n\n    @Test\n    public void testLogicalNor() throws EvaluationException {\n        IValue res1 = Operators.LOGICAL_NOR.evaluate(new IVariable[]{bTrue, bTrue});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class)); // We can't use hamcrest's isA because of: https://github.com/hamcrest/JavaHamcrest/issues/27\n        assertThat(\"true !|| true = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.LOGICAL_NOR.evaluate(new IVariable[]{bTrue, bFalse});\n        assertThat(\"true !|| false = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.LOGICAL_NOR.evaluate(new IVariable[]{bFalse, bTrue});\n        assertThat(\"false !|| true = false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.LOGICAL_NOR.evaluate(new IVariable[]{bFalse, bFalse});\n        assertThat(\"false !|| false = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeNorLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NOR.evaluate(new IVariable[]{bTrue, bTrue, bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputSizeNorSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NOR.evaluate(new IVariable[]{bTrue}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNor() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.LOGICAL_NOR.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    @Test\n    public void testShortCircuitingNor() throws EvaluationException {\n        Operators.LOGICAL_NOR.evaluate(new IVariable[]{bTrue, bFalse});\n        assertThat(\"first variable was called\", bTrue.isFetched(), is(true));\n        assertThat(\"second variable was not called\", bFalse.isFetched(), is(false));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestNbtOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.nbt.*;\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Optional;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different NBT operators.\n *\n * @author rubensworks\n */\npublic class TestNbtOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableString sa;\n    private DummyVariableString sb;\n    private DummyVariableString sc;\n    private DummyVariableString sbyte;\n    private DummyVariableString sshort;\n    private DummyVariableString sinteger;\n    private DummyVariableString slong;\n    private DummyVariableString sfloat;\n    private DummyVariableString sdouble;\n    private DummyVariableString sstring;\n    private DummyVariableString sbytearray;\n    private DummyVariableString sintarray;\n    private DummyVariableString slongarray;\n    private DummyVariableString sboolean;\n    private DummyVariableString scompound;\n    private DummyVariableString slist;\n    private DummyVariableInteger i1;\n    private DummyVariable<ValueTypeLong.ValueLong> l1;\n    private DummyVariableDouble d1_5;\n    private DummyVariableBoolean btrue;\n    private DummyVariableList ltags;\n    private DummyVariableList lints;\n    private DummyVariableList llongs;\n\n    private DummyVariableNbt nempty;\n    private DummyVariableNbt nsasa;\n    private DummyVariableNbt nsbsb;\n    private DummyVariableNbt nsasasbsc;\n    private DummyVariableNbt nall;\n    private DummyVariableNbt nsome;\n\n    private DummyVariableNbt nboolean;\n    private DummyVariableNbt nbyte;\n    private DummyVariableNbt nshort;\n    private DummyVariableNbt nint;\n    private DummyVariableNbt nlong;\n    private DummyVariableNbt ndouble;\n    private DummyVariableNbt nfloat;\n    private DummyVariableNbt nstring;\n    private DummyVariableNbt ntaglist;\n    private DummyVariableNbt nbytelist;\n    private DummyVariableNbt nintlist;\n    private DummyVariableNbt nlonglist;\n\n    @BeforeAll\n    public static void beforeClass() {\n        ValueTypeListProxyFactories.load();\n    }\n\n    @BeforeEach\n    public void before() {\n        sa = new DummyVariableString(ValueTypeString.ValueString.of(\"a\"));\n        sb = new DummyVariableString(ValueTypeString.ValueString.of(\"b\"));\n        sc = new DummyVariableString(ValueTypeString.ValueString.of(\"c\"));\n        sbyte = new DummyVariableString(ValueTypeString.ValueString.of(\"byte\"));\n        sshort = new DummyVariableString(ValueTypeString.ValueString.of(\"short\"));\n        sinteger = new DummyVariableString(ValueTypeString.ValueString.of(\"integer\"));\n        slong = new DummyVariableString(ValueTypeString.ValueString.of(\"long\"));\n        sfloat = new DummyVariableString(ValueTypeString.ValueString.of(\"float\"));\n        sdouble = new DummyVariableString(ValueTypeString.ValueString.of(\"double\"));\n        sstring = new DummyVariableString(ValueTypeString.ValueString.of(\"string\"));\n        sbytearray = new DummyVariableString(ValueTypeString.ValueString.of(\"bytearray\"));\n        sintarray = new DummyVariableString(ValueTypeString.ValueString.of(\"intarray\"));\n        slongarray = new DummyVariableString(ValueTypeString.ValueString.of(\"longarray\"));\n        sboolean = new DummyVariableString(ValueTypeString.ValueString.of(\"boolean\"));\n        scompound = new DummyVariableString(ValueTypeString.ValueString.of(\"compound\"));\n        slist = new DummyVariableString(ValueTypeString.ValueString.of(\"list\"));\n\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        l1 = new DummyVariable<ValueTypeLong.ValueLong>(ValueTypes.LONG, ValueTypeLong.ValueLong.of(1));\n        d1_5 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(1.5D));\n        btrue = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true));\n\n        nempty = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(new CompoundTag()));\n\n        CompoundTag tsasa = new CompoundTag();\n        tsasa.putString(\"a\", \"a\");\n        nsasa = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tsasa));\n\n        CompoundTag tsbsb = new CompoundTag();\n        tsbsb.putString(\"b\", \"b\");\n        nsbsb = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tsbsb));\n\n        CompoundTag tsasasbsc = new CompoundTag();\n        tsasasbsc.putString(\"a\", \"a\");\n        tsasasbsc.putString(\"b\", \"c\");\n        nsasasbsc = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tsasasbsc));\n\n        CompoundTag tall = new CompoundTag();\n        tall.putByte(\"byte\", (byte) 1);\n        tall.putShort(\"short\", (short) 2);\n        tall.putInt(\"integer\", 3);\n        tall.putLong(\"long\", 4L);\n        tall.putFloat(\"float\", 5.5F);\n        tall.putDouble(\"double\", 6.5D);\n        tall.putString(\"string\", \"seven\");\n        tall.putByteArray(\"bytearray\", new byte[]{8, 9, 10});\n        tall.putIntArray(\"intarray\", new int[]{11, 12, 13});\n        tall.putLongArray(\"longarray\", new long[]{14, 15, 16});\n        tall.putBoolean(\"boolean\", true);\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n        tall.put(\"compound\", subCompound);\n        ListTag subList = new ListTag();\n        subList.add(subCompound);\n        subList.add(subCompound);\n        tall.put(\"list\", subList);\n        nall = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tall));\n\n        CompoundTag tsome = new CompoundTag();\n        tsome.putByte(\"byte\", (byte) 1);\n        tsome.putInt(\"integer\", 3);\n        tsome.putFloat(\"float\", 5.5F);\n        tsome.putString(\"string\", \"seven\");\n        tsome.putBoolean(\"boolean\", true);\n        CompoundTag subTagSome = new CompoundTag();\n        subTagSome.putString(\"hello\", \"world\");\n        tsome.put(\"compound\", subTagSome);\n        ListTag subListSome = new ListTag();\n        subListSome.add(subTagSome);\n        tsome.put(\"list\", subListSome);\n        nsome = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tsome));\n\n        ltags = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeNbt.ValueNbt.of(new CompoundTag()),\n                ValueTypeNbt.ValueNbt.of(tsasa),\n                ValueTypeNbt.ValueNbt.of(new CompoundTag())\n        ));\n        lints = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeInteger.ValueInteger.of(5),\n                ValueTypeInteger.ValueInteger.of(4),\n                ValueTypeInteger.ValueInteger.of(3)\n        ));\n        llongs = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeLong.ValueLong.of(5),\n                ValueTypeLong.ValueLong.of(4),\n                ValueTypeLong.ValueLong.of(3)\n        ));\n\n        nboolean = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(ByteTag.ONE));\n        nbyte = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(ByteTag.ONE));\n        nshort = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(ShortTag.valueOf((short) 2)));\n        nint = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(IntTag.valueOf(3)));\n        nlong = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(LongTag.valueOf(4L)));\n        ndouble = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(DoubleTag.valueOf(5.5)));\n        nfloat = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(FloatTag.valueOf(6.5F)));\n        nstring = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(StringTag.valueOf(\"7\")));\n        ntaglist = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(subList));\n        nbytelist = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(new ByteArrayTag(new byte[]{0, 1, 2})));\n        nintlist = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(new IntArrayTag(new int[]{0, 1, 2})));\n        nlonglist = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(new LongArrayTag(new long[]{0, 1, 2})));\n    }\n\n    /**\n     * ----------------------------------- SIZE -----------------------------------\n     */\n\n    @Test\n    public void testNbtSize() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_SIZE.evaluate(new IVariable[]{nempty});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"size({}) = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_SIZE.evaluate(new IVariable[]{nsasa});\n        assertThat(\"size({a:a}) = 1\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(1));\n\n        IValue res3 = Operators.NBT_COMPOUND_SIZE.evaluate(new IVariable[]{nsasasbsc});\n        assertThat(\"size({a:a;b:b}) = 2\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(2));\n    }\n\n    @Test\n    public void testInvalidInputNbtSizeSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_SIZE.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtSizeSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_SIZE.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtSize() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_SIZE.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- KEYS -----------------------------------\n     */\n\n    @Test\n    public void testNbtKeys() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_KEYS.evaluate(new IVariable[]{nempty});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"keys({}).size = 0\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_KEYS.evaluate(new IVariable[]{nsasasbsc});\n        assertThat(\"keys({a:a;b:b}).size = 2\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(2));\n        assertThat(\"keys({a:a;b:b})[0] = a\", ((ValueTypeList.ValueList) res2).getRawValue().get(0), is(ValueTypeString.ValueString.of(\"a\")));\n        assertThat(\"keys({a:a;b:b})[1] = b\", ((ValueTypeList.ValueList) res2).getRawValue().get(1), is(ValueTypeString.ValueString.of(\"b\")));\n    }\n\n    @Test\n    public void testInvalidInputNbtKeysSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_KEYS.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtKeysSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_KEYS.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtKeys() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_KEYS.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- HASKEY -----------------------------------\n     */\n\n    @Test\n    public void testNbtHasKey() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_HASKEY.evaluate(new IVariable[]{nempty, sa});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"haskey({}, a) = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.NBT_COMPOUND_HASKEY.evaluate(new IVariable[]{nsasasbsc, sa});\n        assertThat(\"haskey({a:a}, a) = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputNbtHasKeySizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_HASKEY.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtHasKeySizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_HASKEY.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtHasKey() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_HASKEY.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_TYPE -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueType() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nempty, sa});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"valuetype({}, a) = null\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"null\"));\n\n        assertThat(\"valuetype({...}, byte) = byte\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sbyte})).getRawValue(), is(\"BYTE\"));\n        assertThat(\"valuetype({...}, short) = short\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sshort})).getRawValue(), is(\"SHORT\"));\n        assertThat(\"valuetype({...}, integer) = integer\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sinteger})).getRawValue(), is(\"INT\"));\n        assertThat(\"valuetype({...}, long) = long\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, slong})).getRawValue(), is(\"LONG\"));\n        assertThat(\"valuetype({...}, float) = float\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sfloat})).getRawValue(), is(\"FLOAT\"));\n        assertThat(\"valuetype({...}, double) = double\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sdouble})).getRawValue(), is(\"DOUBLE\"));\n        assertThat(\"valuetype({...}, string) = string\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sstring})).getRawValue(), is(\"STRING\"));\n        assertThat(\"valuetype({...}, bytearray) = bytearray\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sbytearray})).getRawValue(), is(\"BYTE[]\"));\n        assertThat(\"valuetype({...}, intarray) = intarray\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sintarray})).getRawValue(), is(\"INT[]\"));\n        assertThat(\"valuetype({...}, boolean) = byte\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, sboolean})).getRawValue(), is(\"BYTE\"));\n        assertThat(\"valuetype({...}, tag) = tag\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, scompound})).getRawValue(), is(\"COMPOUND\"));\n        assertThat(\"valuetype({...}, list) = list\", ((ValueTypeString.ValueString)\n                Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nall, slist})).getRawValue(), is(\"LIST\"));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueTypeSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueTypeSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_TYPE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_TAG -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueTag() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_TAG.evaluate(new IVariable[]{nempty, sboolean});\n        assertThat(\"result is a tag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"valuetag({}, boolean) = false\", ((ValueTypeNbt.ValueNbt) res1).getRawValue(), is(Optional.empty()));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_TAG.evaluate(new IVariable[]{nall, sboolean});\n        assertThat(\"valuetag({...}, boolean) = true\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(ByteTag.ONE));\n\n        IValue res3 = Operators.NBT_COMPOUND_VALUE_TAG.evaluate(new IVariable[]{nall, sinteger});\n        assertThat(\"valuetag({...}, integer) = true\", ((ValueTypeNbt.ValueNbt) res3).getRawValue().get(), is(IntTag.valueOf(3)));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueTagSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_TAG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueTagSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_TAG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueTag() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_TAG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_BOOLEAN -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueBoolean() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_BOOLEAN.evaluate(new IVariable[]{nempty, sboolean});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"valueboolean({}, boolean) = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_BOOLEAN.evaluate(new IVariable[]{nall, sboolean});\n        assertThat(\"valueboolean({...}, boolean) = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.NBT_COMPOUND_VALUE_BOOLEAN.evaluate(new IVariable[]{nall, sbyte});\n        assertThat(\"valueboolean({...}, byte) = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueBooleanSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_BOOLEAN.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueBooleanSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_BOOLEAN.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueBoolean() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_BOOLEAN.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_INTEGER -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueInteger() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{nempty, sinteger});\n        assertThat(\"result is a integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"valueinteger({}, integer) = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{nall, sinteger});\n        assertThat(\"valueinteger({...}, integer) = 3\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(3));\n\n        IValue res3 = Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{nall, sbyte});\n        assertThat(\"valueinteger({...}, byte) = 3\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(1));\n\n        IValue res4 = Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{nall, sshort});\n        assertThat(\"valueinteger({...}, short) = 2\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(2));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueIntegerSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueIntegerSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueInteger() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_INTEGER.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_LONG -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueLong() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_LONG.evaluate(new IVariable[]{nempty, slong});\n        assertThat(\"result is a long\", res1, instanceOf(ValueTypeLong.ValueLong.class));\n        assertThat(\"valuelong({}, long) = 0\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0L));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_LONG.evaluate(new IVariable[]{nall, slong});\n        assertThat(\"valuelong({...}, long) = 4\", ((ValueTypeLong.ValueLong) res2).getRawValue(), is(4L));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueLongSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LONG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueLongSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LONG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LONG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueDouble() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_DOUBLE.evaluate(new IVariable[]{nempty, sdouble});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"valuedouble({}, double) = 0\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(0D));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_DOUBLE.evaluate(new IVariable[]{nall, sdouble});\n        assertThat(\"valuedouble({...}, double) = 6.5\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(6.5D));\n\n        IValue res3 = Operators.NBT_COMPOUND_VALUE_DOUBLE.evaluate(new IVariable[]{nall, sfloat});\n        assertThat(\"valuedouble({...}, float) = 5.5\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(5.5D));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueDoubleSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_DOUBLE.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueDoubleSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_DOUBLE.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueDouble() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_DOUBLE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_STRING -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueString() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_STRING.evaluate(new IVariable[]{nempty, sstring});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"valuestring({}, string) = \", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"\"));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_STRING.evaluate(new IVariable[]{nall, sstring});\n        assertThat(\"valuestring({...}, string) = seven\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"seven\"));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueStringSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_STRING.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueStringSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_STRING.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueString() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_STRING.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_COMPOUND -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueCompound() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_COMPOUND.evaluate(new IVariable[]{nempty, scompound});\n        assertThat(\"result is a tag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"valuecompound({}, compound) = {}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue(), is(Optional.empty()));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_COMPOUND.evaluate(new IVariable[]{nall, scompound});\n        CompoundTag subTag = new CompoundTag();\n        subTag.putString(\"hello\", \"world\");\n        assertThat(\"valuecompound({...}, compound) = {hello:world}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(subTag));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueCompoundSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_COMPOUND.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueCompoundSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_COMPOUND.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueCompound() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_COMPOUND.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_LIST_TAG -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueListTag() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_LIST_TAG.evaluate(new IVariable[]{nempty, slist});\n        assertThat(\"result is a listtag\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"valuelisttag({}, listtag).size = 0\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_LIST_TAG.evaluate(new IVariable[]{nall, slist});\n        CompoundTag subTag = new CompoundTag();\n        subTag.putString(\"hello\", \"world\");\n        ListTag subList = new ListTag();\n        subList.add(subTag);\n        assertThat(\"valuelisttag({...}, listtag).size = 2\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(2));\n        assertThat(\"valuelisttag({...}, listtag)[0] = ..list..\", ((ValueTypeList.ValueList) res2).getRawValue().get(0), is(ValueTypeNbt.ValueNbt.of(subTag)));\n        assertThat(\"valuelisttag({...}, listtag)[1] = ..list..\", ((ValueTypeList.ValueList) res2).getRawValue().get(1), is(ValueTypeNbt.ValueNbt.of(subTag)));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListTagSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_TAG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListTagSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_TAG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueListTag() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_TAG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_LIST_INT -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueListInt() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_LIST_INT.evaluate(new IVariable[]{nempty, slist});\n        assertThat(\"result is a listint\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"valuelistint({}, listint).size = 0\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_LIST_INT.evaluate(new IVariable[]{nall, sintarray});\n        assertThat(\"valuelistint({...}, listint).size = 3\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(3));\n        assertThat(\"valuelistint({...}, listint)[0] = 11\", ((ValueTypeList.ValueList) res2).getRawValue().get(0), is(ValueTypeInteger.ValueInteger.of(11)));\n        assertThat(\"valuelistint({...}, listint)[1] = 12\", ((ValueTypeList.ValueList) res2).getRawValue().get(1), is(ValueTypeInteger.ValueInteger.of(12)));\n        assertThat(\"valuelistint({...}, listint)[2] = 13\", ((ValueTypeList.ValueList) res2).getRawValue().get(2), is(ValueTypeInteger.ValueInteger.of(13)));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListIntSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_INT.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListIntSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_INT.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueListInt() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_INT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_LIST_LONG -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueListLong() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_LIST_LONG.evaluate(new IVariable[]{nempty, slist});\n        assertThat(\"result is a listlong\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"valuelistlong({}, listlong).size = 0\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_LIST_LONG.evaluate(new IVariable[]{nall, slongarray});\n        assertThat(\"valuelistlong({...}, listlong).size = 3\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(3));\n        assertThat(\"valuelistlong({...}, listlong)[0] = 14\", ((ValueTypeList.ValueList) res2).getRawValue().get(0), is(ValueTypeLong.ValueLong.of(14)));\n        assertThat(\"valuelistlong({...}, listlong)[1] = 15\", ((ValueTypeList.ValueList) res2).getRawValue().get(1), is(ValueTypeLong.ValueLong.of(15)));\n        assertThat(\"valuelistlong({...}, listlong)[2] = 16\", ((ValueTypeList.ValueList) res2).getRawValue().get(2), is(ValueTypeLong.ValueLong.of(16)));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListLongSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_LONG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListLongSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_LONG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueListLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_LONG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- VALUE_LIST_BYTE -----------------------------------\n     */\n\n    @Test\n    public void testNbtValueListByte() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_VALUE_LIST_BYTE.evaluate(new IVariable[]{nempty, slist});\n        assertThat(\"result is a listbyte\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"valuelistbyte({}, listbyte).size = 0\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(0));\n\n        IValue res2 = Operators.NBT_COMPOUND_VALUE_LIST_BYTE.evaluate(new IVariable[]{nall, sbytearray});\n        assertThat(\"valuelistbyte({...}, listbyte).size = 3\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(3));\n        assertThat(\"valuelistbyte({...}, listbyte)[0] = 8\", ((ValueTypeList.ValueList) res2).getRawValue().get(0), is(ValueTypeInteger.ValueInteger.of(8)));\n        assertThat(\"valuelistbyte({...}, listbyte)[1] = 9\", ((ValueTypeList.ValueList) res2).getRawValue().get(1), is(ValueTypeInteger.ValueInteger.of(9)));\n        assertThat(\"valuelistbyte({...}, listbyte)[2] = 10\", ((ValueTypeList.ValueList) res2).getRawValue().get(2), is(ValueTypeInteger.ValueInteger.of(10)));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListByteSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_BYTE.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtValueListByteSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_BYTE.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtValueListByte() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_VALUE_LIST_BYTE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITHOUT -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithout() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITHOUT.evaluate(new IVariable[]{nsasa, sa});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"without({a:a}, a) = {}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(new CompoundTag()));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITHOUT.evaluate(new IVariable[]{nsasasbsc, sa});\n        CompoundTag tsbc = new CompoundTag();\n        tsbc.putString(\"b\", \"c\");\n        assertThat(\"without({a:a;b:c}, a) = {b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(tsbc));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithoutSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITHOUT.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithoutSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITHOUT.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithout() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITHOUT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_BOOLEAN -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithBoolean() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_BOOLEAN.evaluate(new IVariable[]{nsasa, sc, btrue});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putBoolean(\"c\", true);\n        assertThat(\"withboolean({a:a}, c, true) = {a:a,c:true}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_BOOLEAN.evaluate(new IVariable[]{nsasasbsc, sa, btrue});\n        CompoundTag t2 = new CompoundTag();\n        t2.putBoolean(\"a\", true);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withboolean({a:a;b:c}, a, true) = {a:true;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithBooleanSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_BOOLEAN.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithBooleanSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_BOOLEAN.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithBoolean() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_BOOLEAN.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_SHORT -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithShort() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_SHORT.evaluate(new IVariable[]{nsasa, sc, i1});\n        assertThat(\"result is a short\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putShort(\"c\", (short) 1);\n        assertThat(\"withshort({a:a}, c, 1) = {a:a,c:1}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_SHORT.evaluate(new IVariable[]{nsasasbsc, sa, i1});\n        CompoundTag t2 = new CompoundTag();\n        t2.putShort(\"a\", (short) 1);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withshort({a:a;b:c}, a, 1) = {a:1;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithShortSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_SHORT.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithShortSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_SHORT.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithShort() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_SHORT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_INTEGER -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithInteger() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_INTEGER.evaluate(new IVariable[]{nsasa, sc, i1});\n        assertThat(\"result is a integer\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putInt(\"c\", 1);\n        assertThat(\"withinteger({a:a}, c, 1) = {a:a,c:1}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_INTEGER.evaluate(new IVariable[]{nsasasbsc, sa, i1});\n        CompoundTag t2 = new CompoundTag();\n        t2.putInt(\"a\", 1);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withinteger({a:a;b:c}, a, 1) = {a:1;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithIntegerSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_INTEGER.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithIntegerSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_INTEGER.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithInteger() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_INTEGER.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_LONG -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithLong() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_LONG.evaluate(new IVariable[]{nsasa, sc, l1});\n        assertThat(\"result is a long\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putLong(\"c\", 1L);\n        assertThat(\"withlong({a:a}, c, 1) = {a:a,c:1}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_LONG.evaluate(new IVariable[]{nsasasbsc, sa, l1});\n        CompoundTag t2 = new CompoundTag();\n        t2.putLong(\"a\", 1L);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withlong({a:a;b:c}, a, 1) = {a:1;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithLongSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LONG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithLongSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LONG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LONG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithDouble() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_DOUBLE.evaluate(new IVariable[]{nsasa, sc, d1_5});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putDouble(\"c\", 1.5D);\n        assertThat(\"withdouble({a:a}, c, 1.5) = {a:a,c:1.5}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_DOUBLE.evaluate(new IVariable[]{nsasasbsc, sa, d1_5});\n        CompoundTag t2 = new CompoundTag();\n        t2.putDouble(\"a\", 1.5D);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withdouble({a:a;b:c}, a, 1.5) = {a:1.5;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithDoubleSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_DOUBLE.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithDoubleSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_DOUBLE.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithDouble() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_DOUBLE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_FLOAT -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithFloat() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_FLOAT.evaluate(new IVariable[]{nsasa, sc, d1_5});\n        assertThat(\"result is a float\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putFloat(\"c\", 1.5F);\n        assertThat(\"withfloat({a:a}, c, 1.5) = {a:a,c:1.5}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_FLOAT.evaluate(new IVariable[]{nsasasbsc, sa, d1_5});\n        CompoundTag t2 = new CompoundTag();\n        t2.putFloat(\"a\", 1.5F);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withfloat({a:a;b:c}, a, 1.5) = {a:1.5;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithFloatSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_FLOAT.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithFloatSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_FLOAT.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithFloat() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_FLOAT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_STRING -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithString() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_WITH_STRING.evaluate(new IVariable[]{nsasa, sc, sc});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.putString(\"c\", \"c\");\n        assertThat(\"withstring({a:a}, c, c) = {a:a,c:c}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_STRING.evaluate(new IVariable[]{nsasasbsc, sa, sc});\n        CompoundTag t2 = new CompoundTag();\n        t2.putString(\"a\", \"c\");\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withstring({a:a;b:c}, a, c) = {a:c;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithStringSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_STRING.evaluate(new IVariable[]{nempty, sa, i1}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithStringSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_STRING.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithString() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_STRING.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_TAG -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithTag() throws EvaluationException {\n        CompoundTag tsasa = new CompoundTag();\n        tsasa.putString(\"a\", \"a\");\n\n        IValue res1 = Operators.NBT_COMPOUND_WITH_COMPOUND.evaluate(new IVariable[]{nsasa, sc, nsasa});\n        assertThat(\"result is a tag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.put(\"c\", tsasa);\n        assertThat(\"withtag({a:a}, c, tag) = {a:a,c:tag}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_COMPOUND.evaluate(new IVariable[]{nsasasbsc, sa, nsasa});\n        CompoundTag t2 = new CompoundTag();\n        t2.put(\"a\", tsasa);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withtag({a:a;b:c}, a, tag) = {a:tag;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithTagSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_COMPOUND.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithTagSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_COMPOUND.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithTag() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_COMPOUND.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_LIST_TAG -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithListTag() throws EvaluationException {\n        CompoundTag tsasa = new CompoundTag();\n        tsasa.putString(\"a\", \"a\");\n        ListTag tlist = new ListTag();\n        tlist.add(new CompoundTag());\n        tlist.add(tsasa);\n        tlist.add(new CompoundTag());\n\n        IValue res1 = Operators.NBT_COMPOUND_WITH_LIST_TAG.evaluate(new IVariable[]{nsasa, sc, ltags});\n        assertThat(\"result is a listtag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.put(\"c\", tlist);\n        assertThat(\"withlisttag({a:a}, c, listtag) = {a:a,c:listtag}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_LIST_TAG.evaluate(new IVariable[]{nsasasbsc, sa, ltags});\n        CompoundTag t2 = new CompoundTag();\n        t2.put(\"a\", tlist);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withlisttag({a:a;b:c}, a, listtag) = {a:listtag;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListTagSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_TAG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListTagSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_TAG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithListTag() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_TAG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_LIST_BYTE -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithListByte() throws EvaluationException {\n        CompoundTag tsasa = new CompoundTag();\n        tsasa.putString(\"a\", \"a\");\n        ByteArrayTag tlist = new ByteArrayTag(new byte[]{5, 4, 3});\n\n        IValue res1 = Operators.NBT_COMPOUND_WITH_LIST_BYTE.evaluate(new IVariable[]{nsasa, sc, lints});\n        assertThat(\"result is a listbyte\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.put(\"c\", tlist);\n        assertThat(\"withlistbyte({a:a}, c, listbyte) = {a:a,c:listbyte}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_LIST_BYTE.evaluate(new IVariable[]{nsasasbsc, sa, lints});\n        CompoundTag t2 = new CompoundTag();\n        t2.put(\"a\", tlist);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withlistbyte({a:a;b:c}, a, listbyte) = {a:listbyte;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListByteSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_BYTE.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListByteSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_BYTE.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithListByte() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_BYTE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_LIST_INT -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithListInt() throws EvaluationException {\n        CompoundTag tsasa = new CompoundTag();\n        tsasa.putString(\"a\", \"a\");\n        IntArrayTag tlist = new IntArrayTag(new int[]{5, 4, 3});\n\n        IValue res1 = Operators.NBT_COMPOUND_WITH_LIST_INT.evaluate(new IVariable[]{nsasa, sc, lints});\n        assertThat(\"result is a listint\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.put(\"c\", tlist);\n        assertThat(\"withlistint({a:a}, c, listint) = {a:a,c:listint}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_LIST_INT.evaluate(new IVariable[]{nsasasbsc, sa, lints});\n        CompoundTag t2 = new CompoundTag();\n        t2.put(\"a\", tlist);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withlistint({a:a;b:c}, a, listint) = {a:listint;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListIntSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_INT.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListIntSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_INT.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithListInt() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_INT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- WITH_LIST_LONG -----------------------------------\n     */\n\n    @Test\n    public void testNbtWithListLong() throws EvaluationException {\n        CompoundTag tsasa = new CompoundTag();\n        tsasa.putString(\"a\", \"a\");\n        LongArrayTag tlist = new LongArrayTag(new long[]{5, 4, 3});\n\n        IValue res1 = Operators.NBT_COMPOUND_WITH_LIST_LONG.evaluate(new IVariable[]{nsasa, sc, llongs});\n        assertThat(\"result is a listlong\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        CompoundTag t1 = new CompoundTag();\n        t1.putString(\"a\", \"a\");\n        t1.put(\"c\", tlist);\n        assertThat(\"withlistlong({a:a}, c, listlong) = {a:a,c:listlong}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(t1));\n\n        IValue res2 = Operators.NBT_COMPOUND_WITH_LIST_LONG.evaluate(new IVariable[]{nsasasbsc, sa, llongs});\n        CompoundTag t2 = new CompoundTag();\n        t2.put(\"a\", tlist);\n        t2.putString(\"b\", \"c\");\n        assertThat(\"withlistlong({a:a;b:c}, a, listlong) = {a:listlong;b:c}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(t2));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListLongSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_LONG.evaluate(new IVariable[]{nempty, sa, sa}));\n    }\n\n    @Test\n    public void testInvalidInputNbtWithListLongSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_LONG.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtWithListLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_WITH_LIST_LONG.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- SUBSET -----------------------------------\n     */\n\n    @Test\n    public void testNbtSubset() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_SUBSET.evaluate(new IVariable[]{nempty, nall});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"subset({}, all) = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.NBT_COMPOUND_SUBSET.evaluate(new IVariable[]{nall, nempty});\n        assertThat(\"subset(all, {}) = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.NBT_COMPOUND_SUBSET.evaluate(new IVariable[]{nsome, nall});\n        assertThat(\"subset(some, all) = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputNbtSubsetSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_SUBSET.evaluate(new IVariable[]{nempty, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtSubsetSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_SUBSET.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtSubset() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_SUBSET.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- UNION -----------------------------------\n     */\n\n    @Test\n    public void testNbtUnion() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{nempty, nall});\n        assertThat(\"result is an nbt tag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"union({}, all) = all\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nall.getValue().getRawValue().get()));\n\n        IValue res2 = Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{nall, nempty});\n        assertThat(\"union(all, {}) = all\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(nall.getValue().getRawValue().get()));\n\n        IValue res3 = Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{nsome, nall});\n        assertThat(\"union(some, all) = all\", ((ValueTypeNbt.ValueNbt) res3).getRawValue().get(), is(nall.getValue().getRawValue().get()));\n\n        IValue res4 = Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{nsasa, nsbsb});\n        CompoundTag tsasasbsb = new CompoundTag();\n        tsasasbsb.putString(\"a\", \"a\");\n        tsasasbsb.putString(\"b\", \"b\");\n        assertThat(\"union(sasa, sbsb) = sasasbsb\", ((ValueTypeNbt.ValueNbt) res4).getRawValue().get(), is(tsasasbsb));\n    }\n\n    @Test\n    public void testInvalidInputNbtUnionSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{nempty, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtUnionSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtUnion() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_UNION.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- INTERSECTION -----------------------------------\n     */\n\n    @Test\n    public void testNbtIntersection() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nempty, nall});\n        assertThat(\"result is an nbt tag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"intersection({}, all) = {}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(new CompoundTag()));\n\n        IValue res2 = Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nall, nempty});\n        assertThat(\"intersection(all, {}) = {}\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(new CompoundTag()));\n\n        IValue res3 = Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nsome, nall});\n        assertThat(\"intersection(some, all) = some\", ((ValueTypeNbt.ValueNbt) res3).getRawValue().get(), is(nsome.getValue().getRawValue().get()));\n\n        IValue res4 = Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nsasa, nsbsb});\n        assertThat(\"intersection(sasa, sbsb) = {}\", ((ValueTypeNbt.ValueNbt) res4).getRawValue().get(), is(new CompoundTag()));\n\n        IValue res5 = Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nsasa, nsasasbsc});\n        assertThat(\"intersection(sasa, sasasbsc) = {}\", ((ValueTypeNbt.ValueNbt) res5).getRawValue().get(), is(nsasa.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtIntersectionSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nempty, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtIntersectionSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtIntersection() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_INTERSECTION.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MINUS -----------------------------------\n     */\n\n    @Test\n    public void testNbtMinus() throws EvaluationException {\n        IValue res1 = Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nempty, nall});\n        assertThat(\"result is an nbt tag\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"minus({}, all) = {}\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(new CompoundTag()));\n\n        IValue res2 = Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nall, nempty});\n        assertThat(\"minus(all, {}) = all\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().get(), is(nall.getValue().getRawValue().get()));\n\n        IValue res3 = Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nsome, nall});\n        assertThat(\"minus(some, all) = {}\", ((ValueTypeNbt.ValueNbt) res3).getRawValue().get(), is(new CompoundTag()));\n\n        IValue res4 = Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nsasa, nsbsb});\n        assertThat(\"minus(sasa, sbsb) = sasa\", ((ValueTypeNbt.ValueNbt) res4).getRawValue().get(), is(nsasa.getValue().getRawValue().get()));\n\n        IValue res5 = Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nsasasbsc, nsasa});\n        CompoundTag tsbsc = new CompoundTag();\n        tsbsc.putString(\"b\", \"c\");\n        assertThat(\"minus(sasasbsc, sasa) = {}\", ((ValueTypeNbt.ValueNbt) res5).getRawValue().get(), is(tsbsc));\n    }\n\n    @Test\n    public void testInvalidInputNbtMinusSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nempty, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtMinusSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{nempty}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtMinus() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_COMPOUND_MINUS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_BOOLEAN -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsBoolean() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_BOOLEAN.evaluate(new IVariable[]{nboolean});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"as_boolean(true) = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.NBT_AS_BOOLEAN.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_boolean(empty) = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsBooleanSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BOOLEAN.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsBooleanSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BOOLEAN.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsBoolean() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BOOLEAN.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_BYTE -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsByte() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_BYTE.evaluate(new IVariable[]{nbyte});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"as_byte(1) = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.NBT_AS_BYTE.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_byte(empty) = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsByteSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BYTE.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsByteSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BYTE.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsByte() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BYTE.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_SHORT -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsShort() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_SHORT.evaluate(new IVariable[]{nshort});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"as_short(2) = 2\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(2));\n\n        IValue res2 = Operators.NBT_AS_SHORT.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_short(empty) = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsShortSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_SHORT.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsShortSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_SHORT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsShort() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_SHORT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_INT -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsInt() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_INT.evaluate(new IVariable[]{nint});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"as_int(3) = 3\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(3));\n\n        IValue res2 = Operators.NBT_AS_INT.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_int(empty) = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsIntSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_INT.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsIntSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_INT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsInt() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_INT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_LONG -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsLong() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_LONG.evaluate(new IVariable[]{nlong});\n        assertThat(\"result is a long\", res1, instanceOf(ValueTypeLong.ValueLong.class));\n        assertThat(\"as_long(4) = 4\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(4L));\n\n        IValue res2 = Operators.NBT_AS_LONG.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_long(empty) = 0\", ((ValueTypeLong.ValueLong) res2).getRawValue(), is(0L));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsLongSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_LONG.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsLongSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_LONG.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_LONG.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsDouble() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_DOUBLE.evaluate(new IVariable[]{ndouble});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"as_double(5.5) = 5.5\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(5.5D));\n\n        IValue res2 = Operators.NBT_AS_DOUBLE.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_double(empty) = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsDoubleSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_DOUBLE.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsDoubleSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_DOUBLE.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsDouble() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_DOUBLE.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_FLOAT -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsFloat() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_FLOAT.evaluate(new IVariable[]{nfloat});\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"as_float(6.5) = 6.5\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(6.5D));\n\n        IValue res2 = Operators.NBT_AS_FLOAT.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_float(empty) = 0\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(0D));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsFloatSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_FLOAT.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsFloatSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_FLOAT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsFloat() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_FLOAT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_STRING -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsString() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_STRING.evaluate(new IVariable[]{nstring});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"as_string(7) = 7\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"7\"));\n\n        IValue res2 = Operators.NBT_AS_STRING.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_string(empty) = \", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"\"));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsStringSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_STRING.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsStringSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_STRING.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsString() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_STRING.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_TAG_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsTagList() throws EvaluationException {\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n\n        IValue res1 = Operators.NBT_AS_TAG_LIST.evaluate(new IVariable[]{ntaglist});\n        assertThat(\"result is a taglist\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"as_taglist(...).type = nbt\", ((ValueTypeList.ValueList) res1).getRawValue().getValueType(), is(ValueTypes.NBT));\n        assertThat(\"as_taglist(...).length = 2\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(2));\n        assertThat(\"as_taglist(...)[0] = tag\", ((ValueTypeList.ValueList) res1).getRawValue().get(0), is(ValueTypeNbt.ValueNbt.of(subCompound)));\n        assertThat(\"as_taglist(...)[1] = tag\", ((ValueTypeList.ValueList) res1).getRawValue().get(1), is(ValueTypeNbt.ValueNbt.of(subCompound)));\n\n        IValue res2 = Operators.NBT_AS_TAG_LIST.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_taglist(...).length = 0\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsTagListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_TAG_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsTagListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_TAG_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsTagList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_TAG_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_BYTE_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsByteList() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_BYTE_LIST.evaluate(new IVariable[]{nbytelist});\n        assertThat(\"result is a bytelist\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"as_ bytelist(...).type = integer\", ((ValueTypeList.ValueList) res1).getRawValue().getValueType(), is(ValueTypes.INTEGER));\n        assertThat(\"as_ bytelist(...).length = 3\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(3));\n        assertThat(\"as_ bytelist(...)[0] = byte\", ((ValueTypeList.ValueList) res1).getRawValue().get(0), is(ValueTypeInteger.ValueInteger.of(0)));\n        assertThat(\"as_ bytelist(...)[1] = byte\", ((ValueTypeList.ValueList) res1).getRawValue().get(1), is(ValueTypeInteger.ValueInteger.of(1)));\n        assertThat(\"as_ bytelist(...)[2] = byte\", ((ValueTypeList.ValueList) res1).getRawValue().get(2), is(ValueTypeInteger.ValueInteger.of(2)));\n\n        IValue res2 = Operators.NBT_AS_BYTE_LIST.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_ bytelist(...).length = 0\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsByteListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BYTE_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsByteListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BYTE_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsByteList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_BYTE_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_INT_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsIntList() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_INT_LIST.evaluate(new IVariable[]{nintlist});\n        assertThat(\"result is a intlist\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"as_ intlist(...).type = integer\", ((ValueTypeList.ValueList) res1).getRawValue().getValueType(), is(ValueTypes.INTEGER));\n        assertThat(\"as_ intlist(...).length = 3\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(3));\n        assertThat(\"as_ intlist(...)[0] = int\", ((ValueTypeList.ValueList) res1).getRawValue().get(0), is(ValueTypeInteger.ValueInteger.of(0)));\n        assertThat(\"as_ intlist(...)[1] = int\", ((ValueTypeList.ValueList) res1).getRawValue().get(1), is(ValueTypeInteger.ValueInteger.of(1)));\n        assertThat(\"as_ intlist(...)[2] = int\", ((ValueTypeList.ValueList) res1).getRawValue().get(2), is(ValueTypeInteger.ValueInteger.of(2)));\n\n        IValue res2 = Operators.NBT_AS_INT_LIST.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_ intlist(...).length = 0\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsIntListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_INT_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsIntListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_INT_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsIntList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_INT_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- AS_LONG_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtAsLongList() throws EvaluationException {\n        IValue res1 = Operators.NBT_AS_LONG_LIST.evaluate(new IVariable[]{nlonglist});\n        assertThat(\"result is a longlist\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"as_ longlist(...).type = long\", ((ValueTypeList.ValueList) res1).getRawValue().getValueType(), is(ValueTypes.LONG));\n        assertThat(\"as_ longlist(...).length = 3\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(3));\n        assertThat(\"as_ longlist(...)[0] = long\", ((ValueTypeList.ValueList) res1).getRawValue().get(0), is(ValueTypeLong.ValueLong.of(0)));\n        assertThat(\"as_ longlist(...)[1] = long\", ((ValueTypeList.ValueList) res1).getRawValue().get(1), is(ValueTypeLong.ValueLong.of(1)));\n        assertThat(\"as_ longlist(...)[2] = long\", ((ValueTypeList.ValueList) res1).getRawValue().get(2), is(ValueTypeLong.ValueLong.of(2)));\n\n        IValue res2 = Operators.NBT_AS_LONG_LIST.evaluate(new IVariable[]{nempty});\n        assertThat(\"as_ longlist(...).length = 0\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsLongListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_LONG_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtAsLongListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_LONG_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtAsLongList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_AS_LONG_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_BOOLEAN -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromBoolean() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_BOOLEAN.evaluate(new IVariable[]{new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_boolean(true) = nbt(true)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nboolean.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromBooleanSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BOOLEAN.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromBooleanSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BOOLEAN.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromBoolean() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BOOLEAN.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_BYTE -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromByte() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_BYTE.evaluate(new IVariable[]{new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_byte(1) = nbt(1)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nbyte.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromByteSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BYTE.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromByteSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BYTE.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromByte() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BYTE.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_SHORT -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromShort() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_SHORT.evaluate(new IVariable[]{new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_short(2) = nbt(2)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nshort.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromShortSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_SHORT.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromShortSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_SHORT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromShort() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_SHORT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_INT -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromInt() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_INT.evaluate(new IVariable[]{new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(3))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_int(3) = nbt(3)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nint.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromIntSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_INT.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromIntSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_INT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromInt() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_INT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_LONG -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromLong() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_LONG.evaluate(new IVariable[]{new DummyVariable(ValueTypes.LONG, ValueTypeLong.ValueLong.of(4))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_long(4) = nbt(4)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nlong.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromLongSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_LONG.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromLongSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_LONG.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_LONG.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromDouble() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_DOUBLE.evaluate(new IVariable[]{new DummyVariable(ValueTypes.DOUBLE, ValueTypeDouble.ValueDouble.of(5.5D))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_double(5.5) = nbt(5.5)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(ndouble.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromDoubleSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_DOUBLE.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromDoubleSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_DOUBLE.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromDouble() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_DOUBLE.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_FLOAT -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromFloat() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_FLOAT.evaluate(new IVariable[]{new DummyVariable(ValueTypes.DOUBLE, ValueTypeDouble.ValueDouble.of(6.5D))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_float(6.5) = nbt(6.5)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nfloat.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromFloatSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_FLOAT.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromFloatSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_FLOAT.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromFloat() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_FLOAT.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_STRING -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromString() throws EvaluationException {\n        IValue res1 = Operators.NBT_FROM_STRING.evaluate(new IVariable[]{new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"7\"))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_string(7) = nbt(7)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nstring.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromStringSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_STRING.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromStringSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_STRING.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromString() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_STRING.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_TAG_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromTagList() throws EvaluationException {\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n        IValue res1 = Operators.NBT_FROM_TAG_LIST.evaluate(new IVariable[]{new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeNbt.ValueNbt.of(subCompound),\n                ValueTypeNbt.ValueNbt.of(subCompound)\n        ))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_taglist(7) = nbt(7)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(ntaglist.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromTagListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_TAG_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromTagListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_TAG_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromTagList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_TAG_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_BYTE_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromByteList() throws EvaluationException {\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n        IValue res1 = Operators.NBT_FROM_BYTE_LIST.evaluate(new IVariable[]{new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeInteger.ValueInteger.of(0),\n                ValueTypeInteger.ValueInteger.of(1),\n                ValueTypeInteger.ValueInteger.of(2)\n        ))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_bytelist(7) = nbt(7)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nbytelist.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromByteListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BYTE_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromByteListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BYTE_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromByteList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_BYTE_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_INT_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromIntList() throws EvaluationException {\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n        IValue res1 = Operators.NBT_FROM_INT_LIST.evaluate(new IVariable[]{new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeInteger.ValueInteger.of(0),\n                ValueTypeInteger.ValueInteger.of(1),\n                ValueTypeInteger.ValueInteger.of(2)\n        ))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_intlist(7) = nbt(7)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nintlist.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testNbtFromIntListAny() throws EvaluationException {\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n        IValue res1 = Operators.NBT_FROM_INT_LIST.evaluate(new IVariable[]{new DummyVariableList(ValueTypeList.ValueList.ofList(ValueTypes.CATEGORY_ANY, Lists.newArrayList(\n                ValueTypeInteger.ValueInteger.of(0),\n                ValueTypeInteger.ValueInteger.of(1),\n                ValueTypeInteger.ValueInteger.of(2)\n        )))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_intlist(7) = nbt(7)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nintlist.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromIntListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_INT_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromIntListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_INT_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromIntList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_INT_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FROM_LONG_LIST -----------------------------------\n     */\n\n    @Test\n    public void testNbtFromLongList() throws EvaluationException {\n        CompoundTag subCompound = new CompoundTag();\n        subCompound.putString(\"hello\", \"world\");\n        IValue res1 = Operators.NBT_FROM_LONG_LIST.evaluate(new IVariable[]{new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeLong.ValueLong.of(0),\n                ValueTypeLong.ValueLong.of(1),\n                ValueTypeLong.ValueLong.of(2)\n        ))});\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"from_longlist(7) = nbt(7)\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(nlonglist.getValue().getRawValue().get()));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromLongListSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_LONG_LIST.evaluate(new IVariable[]{nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtFromLongListSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_LONG_LIST.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtFromLongList() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_FROM_LONG_LIST.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- PATH_MATCH_FIRST -----------------------------------\n     */\n\n    @Test\n    public void testNbtPathMatchFirst() throws EvaluationException {\n        CompoundTag tag2 = new CompoundTag();\n        CompoundTag tag3 = new CompoundTag();\n        StringTag tag4 = StringTag.valueOf(\"x\");\n        tag2.put(\"a\", tag3);\n        tag3.put(\"b\", tag4);\n\n        IValue res1 = Operators.NBT_PATH_MATCH_FIRST.evaluate(new IVariable[]{\n                new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"$.a.b\")),\n                new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tag2))\n        });\n        assertThat(\"result is nbt\", res1, instanceOf(ValueTypeNbt.ValueNbt.class));\n        assertThat(\"path_match_first(...) = x\", ((ValueTypeNbt.ValueNbt) res1).getRawValue().get(), is(tag4));\n\n        IValue res2 = Operators.NBT_PATH_MATCH_FIRST.evaluate(new IVariable[]{\n                new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"$.a.b\")),\n                nempty\n        });\n        assertThat(\"path_match_first(empty) = empty\", ((ValueTypeNbt.ValueNbt) res2).getRawValue().isPresent(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathMatchFirstInvalidPathExpression() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_FIRST.evaluate(new IVariable[]{nstring, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathMatchFirstSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_FIRST.evaluate(new IVariable[]{nstring, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathMatchFirstSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_FIRST.evaluate(new IVariable[]{nstring}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtPathMatchFirst() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_FIRST.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- PATH_MATCH_ALL -----------------------------------\n     */\n\n    @Test\n    public void testNbtPathMatchAll() throws EvaluationException {\n        CompoundTag tag2 = new CompoundTag();\n        CompoundTag tag3 = new CompoundTag();\n        StringTag tag4 = StringTag.valueOf(\"x\");\n        StringTag tag5 = StringTag.valueOf(\"y\");\n        tag2.put(\"a\", tag3);\n        tag3.put(\"b\", tag4);\n        tag3.put(\"c\", tag5);\n\n        IValue res1 = Operators.NBT_PATH_MATCH_ALL.evaluate(new IVariable[]{\n                new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"$.a*\")),\n                new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tag2))\n        });\n        assertThat(\"result is list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"path_match_all(...).type = NBT\", ((ValueTypeList.ValueList) res1).getRawValue().getValueType(), is(ValueTypes.NBT));\n        assertThat(\"path_match_all(...).length = 2\", ((ValueTypeList.ValueList) res1).getRawValue().getLength(), is(2));\n        assertThat(\"path_match_all(...)[0] = x\", ((ValueTypeList.ValueList) res1).getRawValue().get(0), is(ValueTypeNbt.ValueNbt.of(tag4)));\n        assertThat(\"path_match_all(...)[1] = y\", ((ValueTypeList.ValueList) res1).getRawValue().get(1), is(ValueTypeNbt.ValueNbt.of(tag5)));\n\n        IValue res2 = Operators.NBT_PATH_MATCH_ALL.evaluate(new IVariable[]{\n                new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"$.a*\")),\n                nempty\n        });\n        assertThat(\"path_match_all(empty).length = 0\", ((ValueTypeList.ValueList) res2).getRawValue().getLength(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathMatchAllInvalidPathExpression() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_ALL.evaluate(new IVariable[]{nstring, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathMatchAllSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_ALL.evaluate(new IVariable[]{nstring, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathMatchAllSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_ALL.evaluate(new IVariable[]{nstring}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtPathMatchAll() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_MATCH_ALL.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- PATH_TEST -----------------------------------\n     */\n\n    @Test\n    public void testNbtPathTest() throws EvaluationException {\n        CompoundTag tag2 = new CompoundTag();\n        CompoundTag tag3 = new CompoundTag();\n        StringTag tag4 = StringTag.valueOf(\"x\");\n        tag2.put(\"a\", tag3);\n        tag3.put(\"b\", tag4);\n\n        IValue res1 = Operators.NBT_PATH_TEST.evaluate(new IVariable[]{\n                new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"$.a.b\")),\n                new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tag2))\n        });\n        assertThat(\"result is boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"path_test(...) = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.NBT_PATH_TEST.evaluate(new IVariable[]{\n                new DummyVariable(ValueTypes.STRING, ValueTypeString.ValueString.of(\"$.a.b\")),\n                nempty\n        });\n        assertThat(\"path_test(empty) = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathTestInvalidPathExpression() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_TEST.evaluate(new IVariable[]{nstring, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathTestSizeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_TEST.evaluate(new IVariable[]{nstring, nempty, nempty}));\n    }\n\n    @Test\n    public void testInvalidInputNbtPathTestSizeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_TEST.evaluate(new IVariable[]{nstring}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNbtPathTest() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NBT_PATH_TEST.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestNumberOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different number operators.\n *\n * @author rubensworks\n */\npublic class TestNumberOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableDouble d0;\n    private DummyVariableDouble dm10;\n    private DummyVariableDouble d0P5;\n    private DummyVariableDouble d0P1;\n    private DummyVariableDouble d0P9;\n    private DummyVariableInteger i10;\n    private DummyVariableInteger i1k;\n    private DummyVariableInteger i1m;\n\n    @BeforeEach\n    public void before() {\n        d0 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0));\n        dm10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(-10));\n        d0P5 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0.5));\n        d0P1 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0.1));\n        d0P9 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0.9));\n\n        i10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10));\n        i1k = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1000));\n        i1m = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1000000));\n    }\n\n    /**\n     * ----------------------------------- ROUND -----------------------------------\n     */\n\n    @Test\n    public void testDoubleRound() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_ROUND.evaluate(new IVariable[]{d0});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"||0|| = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.NUMBER_ROUND.evaluate(new IVariable[]{dm10});\n        assertThat(\"||-10|| = -10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(-10));\n\n        IValue res4 = Operators.NUMBER_ROUND.evaluate(new IVariable[]{d0P5});\n        assertThat(\"||0.5|| = 1\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(1));\n\n        IValue res5 = Operators.NUMBER_ROUND.evaluate(new IVariable[]{d0P1});\n        assertThat(\"||0.1|| = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n\n        IValue res6 = Operators.NUMBER_ROUND.evaluate(new IVariable[]{d0P9});\n        assertThat(\"||0.9|| = 1\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(1));\n\n        IValue res7 = Operators.NUMBER_ROUND.evaluate(new IVariable[]{i10});\n        assertThat(\"||10|| = 10\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(10));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleRoundLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_ROUND.evaluate(new IVariable[]{d0, d0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleRoundSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_ROUND.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeDoubleRound() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_ROUND.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CEIL -----------------------------------\n     */\n\n    @Test\n    public void testDoubleCeil() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_CEIL.evaluate(new IVariable[]{d0});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"⌈0⌉ = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.NUMBER_CEIL.evaluate(new IVariable[]{dm10});\n        assertThat(\"⌈-10⌉ = -10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(-10));\n\n        IValue res4 = Operators.NUMBER_CEIL.evaluate(new IVariable[]{d0P5});\n        assertThat(\"⌈0.5⌉ = 1\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(1));\n\n        IValue res5 = Operators.NUMBER_CEIL.evaluate(new IVariable[]{d0P1});\n        assertThat(\"⌈0.1⌉ = 1\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(1));\n\n        IValue res6 = Operators.NUMBER_CEIL.evaluate(new IVariable[]{d0P9});\n        assertThat(\"⌈0.9⌉ = 1\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(1));\n\n        IValue res7 = Operators.NUMBER_CEIL.evaluate(new IVariable[]{i10});\n        assertThat(\"⌈10⌉ = 10\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(10));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleCeilLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CEIL.evaluate(new IVariable[]{d0, d0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleCeilSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CEIL.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeDoubleCeil() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CEIL.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FLOOR -----------------------------------\n     */\n\n    @Test\n    public void testDoubleFloor() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_FLOOR.evaluate(new IVariable[]{d0});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"⌊0⌋ = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n\n        IValue res2 = Operators.NUMBER_FLOOR.evaluate(new IVariable[]{dm10});\n        assertThat(\"⌊-10⌋ = -10\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(-10));\n\n        IValue res4 = Operators.NUMBER_FLOOR.evaluate(new IVariable[]{d0P5});\n        assertThat(\"⌊0.5⌋ = 0\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(0));\n\n        IValue res5 = Operators.NUMBER_FLOOR.evaluate(new IVariable[]{d0P1});\n        assertThat(\"⌊0.1⌋ = 0\", ((ValueTypeInteger.ValueInteger) res5).getRawValue(), is(0));\n\n        IValue res6 = Operators.NUMBER_FLOOR.evaluate(new IVariable[]{d0P9});\n        assertThat(\"⌊0.9⌋ = 0\", ((ValueTypeInteger.ValueInteger) res6).getRawValue(), is(0));\n\n        IValue res7 = Operators.NUMBER_FLOOR.evaluate(new IVariable[]{i10});\n        assertThat(\"⌊10⌋ = 10\", ((ValueTypeInteger.ValueInteger) res7).getRawValue(), is(10));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleFloorLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_FLOOR.evaluate(new IVariable[]{d0, d0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleFloorSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_FLOOR.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeDoubleFloor() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_FLOOR.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- FUZZY -----------------------------------\n     */\n    @Test\n    public void testNumberCompact() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_COMPACT.evaluate(new IVariable[]{d0});\n        assertThat(\"compact(0) = 0\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"0\"));\n\n        IValue res2 = Operators.NUMBER_COMPACT.evaluate(new IVariable[]{i10});\n        assertThat(\"compact(10) = 10\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"10\"));\n\n        IValue res3 = Operators.NUMBER_COMPACT.evaluate(new IVariable[]{i1k});\n        assertThat(\"compact(1000) = 1K\", ((ValueTypeString.ValueString) res3).getRawValue(), is(\"1K\"));\n\n        IValue res4 = Operators.NUMBER_COMPACT.evaluate(new IVariable[]{i1m});\n        assertThat(\"compact(1000000) = 1M\", ((ValueTypeString.ValueString) res4).getRawValue(), is(\"1M\"));\n    }\n\n    /**\n     * ----------------------------------- CAST_DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testDoubleCastDouble() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_CAST_DOUBLE.evaluate(new Variable<>(ValueTypeDouble.ValueDouble.of(1.1D)));\n        assertThat(\"result is a double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"cast 1.1D = 1.1D\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(1.1D));\n\n        IValue res2 = Operators.NUMBER_CAST_DOUBLE.evaluate(new Variable<>(ValueTypeLong.ValueLong.of(100L)));\n        assertThat(\"result is a double\", res2, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"cast 100L = 100.0D\", ((ValueTypeDouble.ValueDouble) res2).getRawValue(), is(100.0D));\n\n        IValue res3 = Operators.NUMBER_CAST_DOUBLE.evaluate(new Variable<>(ValueTypeInteger.ValueInteger.of(10)));\n        assertThat(\"result is a double\", res3, instanceOf(ValueTypeDouble.ValueDouble.class));\n        assertThat(\"cast 10 = 10D\", ((ValueTypeDouble.ValueDouble) res3).getRawValue(), is(10.0D));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleCastDoubleLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_DOUBLE.evaluate(d0, d0));\n    }\n\n    @Test\n    public void testInvalidInputSizeDoubleCastDoubleSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_DOUBLE.evaluate());\n    }\n\n    @Test\n    public void testInvalidInputTypeDoubleCastDouble() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_DOUBLE.evaluate(DUMMY_VARIABLE));\n    }\n\n    /**\n     * ----------------------------------- CAST_LONG -----------------------------------\n     */\n\n    @Test\n    public void testLongCastLong() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_CAST_LONG.evaluate(new Variable<>(ValueTypeDouble.ValueDouble.of(1.1D)));\n        assertThat(\"result is a long\", res1, instanceOf(ValueTypeLong.ValueLong.class));\n        assertThat(\"cast 1.1D = 1L\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(1L));\n\n        IValue res2 = Operators.NUMBER_CAST_LONG.evaluate(new Variable<>(ValueTypeLong.ValueLong.of(100L)));\n        assertThat(\"result is a long\", res2, instanceOf(ValueTypeLong.ValueLong.class));\n        assertThat(\"cast 100L = 100L\", ((ValueTypeLong.ValueLong) res2).getRawValue(), is(100L));\n\n        IValue res3 = Operators.NUMBER_CAST_LONG.evaluate(new Variable<>(ValueTypeInteger.ValueInteger.of(10)));\n        assertThat(\"result is a long\", res3, instanceOf(ValueTypeLong.ValueLong.class));\n        assertThat(\"cast 10 = 10L\", ((ValueTypeLong.ValueLong) res3).getRawValue(), is(10L));\n    }\n\n    @Test\n    public void testInvalidInputSizeLongCastLongLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_LONG.evaluate(d0, d0));\n    }\n\n    @Test\n    public void testInvalidInputSizeLongCastLongSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_LONG.evaluate());\n    }\n\n    @Test\n    public void testInvalidInputTypeLongCastLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_LONG.evaluate(DUMMY_VARIABLE));\n    }\n\n    /**\n     * ----------------------------------- CAST_INTEGER -----------------------------------\n     */\n\n    @Test\n    public void testIntegerCastInteger() throws EvaluationException {\n        IValue res1 = Operators.NUMBER_CAST_INTEGER.evaluate(new Variable<>(ValueTypeDouble.ValueDouble.of(1.1D)));\n        assertThat(\"result is a integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"cast 1.1D = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n\n        IValue res2 = Operators.NUMBER_CAST_INTEGER.evaluate(new Variable<>(ValueTypeLong.ValueLong.of(100L)));\n        assertThat(\"result is a integer\", res2, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"cast 100L = 100\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(100));\n\n        IValue res3 = Operators.NUMBER_CAST_INTEGER.evaluate(new Variable<>(ValueTypeInteger.ValueInteger.of(10)));\n        assertThat(\"result is a integer\", res3, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"cast 10 = 10\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(10));\n    }\n\n    @Test\n    public void testInvalidInputSizeIntegerCastIntegerLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_INTEGER.evaluate(d0, d0));\n    }\n\n    @Test\n    public void testInvalidInputSizeIntegerCastIntegerSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_INTEGER.evaluate());\n    }\n\n    @Test\n    public void testInvalidInputTypeIntegerCastInteger() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NUMBER_CAST_INTEGER.evaluate(DUMMY_VARIABLE));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestOperatorOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.CurriedOperator;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.OperatorBase;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.hamcrest.CoreMatchers;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.*;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different integer operators.\n *\n * @author rubensworks\n */\npublic class TestOperatorOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableBoolean bFalse;\n    private DummyVariableBoolean bTrue;\n\n    private DummyVariableString s1;\n\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger i2;\n    private DummyVariableInteger i3;\n    private DummyVariableInteger i4;\n    private DummyVariable i4Any;\n\n    private DummyVariableOperator oGeneralIdentity;\n    private DummyVariableOperator oLogicalNot;\n    private DummyVariableOperator oLogicalAnd;\n    private DummyVariableOperator oParseInt;\n    private DummyVariableOperator oRelationalEquals;\n    private DummyVariableOperator oRelationalGreaterThan;\n    private DummyVariableOperator oRelationalLessThan;\n    private DummyVariableOperator oArithmeticIncrement;\n    private DummyVariableOperator oArithmeticModulus;\n    private DummyVariableOperator oArithmeticAddition;\n    private DummyVariableOperator oArithmeticMultiplication;\n    private DummyVariableOperator oChoice;\n    private DummyVariableOperator oPipe;\n    private DummyVariableOperator oListLength;\n    private DummyVariableOperator oListContains;\n    private DummyVariableOperator oOperatorMap;\n    private DummyVariableOperator oSubStr;\n    private DummyVariableOperator oGetConstStr;\n\n    private DummyVariableList lempty;\n    private DummyVariableList lintegers;\n    private DummyVariableList lbooleans;\n\n    private DummyVariableString sAnd;\n    private DummyVariableString sOther;\n\n    @BeforeEach\n    public void before() {\n        ValueTypeListProxyFactories.load();\n\n        bFalse = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(false));\n        bTrue = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true));\n\n        s1 = new DummyVariableString(ValueTypeString.ValueString.of(\"1\"));\n\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        i2 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2));\n        i3 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(3));\n        i4 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(4));\n        i4Any = new DummyVariable<>(ValueTypes.CATEGORY_ANY, ValueTypeInteger.ValueInteger.of(4));\n\n        oGeneralIdentity = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.GENERAL_IDENTITY));\n        oLogicalNot = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.LOGICAL_NOT));\n        oLogicalAnd = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.LOGICAL_AND));\n        oParseInt = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.PARSE_INTEGER));\n        oRelationalEquals = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.RELATIONAL_EQUALS));\n        oRelationalGreaterThan = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.RELATIONAL_GT));\n        oRelationalLessThan = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.RELATIONAL_LT));\n        oArithmeticIncrement = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_INCREMENT));\n        oArithmeticModulus = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_MODULUS));\n        oArithmeticAddition = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_ADDITION));\n        oArithmeticMultiplication = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.ARITHMETIC_MULTIPLICATION));\n        oChoice = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.GENERAL_CHOICE));\n        oPipe = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_PIPE));\n        oListLength = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.LIST_LENGTH));\n        oListContains = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.LIST_CONTAINS));\n        oOperatorMap = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_MAP));\n        oSubStr = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.STRING_SUBSTRING));\n        oGetConstStr = new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(new OperatorBase(\"NONE\", \"NONE\", \"NONE\", \"NONE\", false, new IValueType[0], ValueTypes.STRING, variables -> ValueTypeString.ValueString.of(\"HI\"), null) {\n            @Override\n            protected String getUnlocalizedType() {\n                return \"NONE\";\n            }\n        }));\n\n        lempty = new DummyVariableList(ValueTypeList.ValueList.ofAll());\n        lintegers = new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), i2.getValue(), i3.getValue()));\n        lbooleans = new DummyVariableList(ValueTypeList.ValueList.ofAll(bFalse.getValue(), bTrue.getValue(), bFalse.getValue(), bTrue.getValue()));\n\n        sAnd = new DummyVariableString(ValueTypeString.ValueString.of(\"integrateddynamics:logical_and\"));\n        sOther = new DummyVariableString(ValueTypeString.ValueString.of(\"integrateddynamics:other\"));\n    }\n\n    /**\n     * ----------------------------------- APPLY -----------------------------------\n     */\n\n    @Test\n    public void testApply() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oGeneralIdentity, bFalse});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"id(false) == false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oGeneralIdentity, bTrue});\n        assertThat(\"id(true) == true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalNot, bTrue});\n        assertThat(\"not(true) == false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalNot, bFalse});\n        assertThat(\"not(true) == false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testApplyCurring() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bTrue});\n        assertThat(\"result is an operator\", res1, instanceOf(ValueTypeOperator.ValueOperator.class));\n        assertThat(\"result is a curriedoperator\", ((ValueTypeOperator.ValueOperator) res1).getRawValue(), instanceOf(CurriedOperator.class));\n\n        DummyVariableOperator oLogicalAndCurriedTrue = new DummyVariableOperator((ValueTypeOperator.ValueOperator) res1);\n        IValue res2_1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAndCurriedTrue, bTrue});\n        assertThat(\"result is a boolean\", res2_1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"and(true)(true) == true\", ((ValueTypeBoolean.ValueBoolean) res2_1).getRawValue(), is(true));\n        IValue res2_2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAndCurriedTrue, bFalse});\n        assertThat(\"and(true)(false) == false\", ((ValueTypeBoolean.ValueBoolean) res2_2).getRawValue(), is(false));\n\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bFalse});\n        assertThat(\"result is a curriedoperator\", ((ValueTypeOperator.ValueOperator) res3).getRawValue(), instanceOf(CurriedOperator.class));\n\n        DummyVariableOperator oLogicalAndCurriedFalse = new DummyVariableOperator((ValueTypeOperator.ValueOperator) res3);\n        IValue res4_1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAndCurriedFalse, bTrue});\n        assertThat(\"result is a boolean\", res2_1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"and(false)(true) == false\", ((ValueTypeBoolean.ValueBoolean) res4_1).getRawValue(), is(false));\n        IValue res4_2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAndCurriedFalse, bFalse});\n        assertThat(\"and(false)(false) == false\", ((ValueTypeBoolean.ValueBoolean) res4_2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeApplyLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oGeneralIdentity, bFalse, bFalse}));\n    }\n\n    @Test\n    public void testInvalidInputSizeApplyCurryingLarge() throws EvaluationException {\n        DummyVariableOperator curriedOperatorValue = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bTrue}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{curriedOperatorValue, bFalse, bFalse}));\n    }\n\n    @Test\n    public void testInvalidInputSizeApplySmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oGeneralIdentity}));\n    }\n\n    @Test\n    public void testInvalidInputSizeApplyCurringSmall() throws EvaluationException {\n        DummyVariableOperator curriedOperatorValue = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bTrue}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{curriedOperatorValue}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypeApply() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{bFalse, bFalse}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeApply() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalNot, oGeneralIdentity}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeAnyApply() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(new IVariable[]{i4Any, i4Any}));\n    }\n\n    @Test\n    public void testValidateTypesApply() {\n        assertThat(Operators.OPERATOR_APPLY.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY.validateTypes(new IValueType[]{ValueTypes.INTEGER}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesApply() {\n        assertThat(Operators.OPERATOR_APPLY.getConditionalOutputType(new IVariable[]{oGeneralIdentity, bFalse}),\n                CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n        assertThat(Operators.OPERATOR_APPLY.getConditionalOutputType(new IVariable[]{oGeneralIdentity, i0}),\n                CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n        assertThat(Operators.OPERATOR_APPLY.getConditionalOutputType(new IVariable[]{oGeneralIdentity, oGeneralIdentity}),\n                CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n\n        assertThat(Operators.OPERATOR_APPLY.getConditionalOutputType(new IVariable[]{oLogicalNot, bFalse}),\n                CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n\n        // In some cases, validation can succeed because of parameters being ANY.\n        // In this case, the tested method should be able to handle ANY variables with possibly non-operator values.\n        assertThat(Operators.OPERATOR_APPLY.getConditionalOutputType(new IVariable[]{i4Any, i4Any}),\n                CoreMatchers.<IValueType>is(ValueTypes.CATEGORY_ANY));\n    }\n\n    @Test\n    public void testConditionalOutputTypesApplyCurring() throws EvaluationException {\n        DummyVariableOperator curriedOperatorValue = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bTrue}));\n\n        assertThat(Operators.OPERATOR_APPLY.getConditionalOutputType(new IVariable[]{curriedOperatorValue, bFalse}),\n                CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n    }\n\n    /**\n     * ----------------------------------- APPLY_2 -----------------------------------\n     */\n\n    @Test\n    public void testApply2() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oLogicalAnd, bTrue, bFalse});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"and(true, false) == false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oLogicalAnd, bTrue, bTrue});\n        assertThat(\"and(true, true) == true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oRelationalGreaterThan, i0, i1});\n        assertThat(\"0 > 1 == false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oRelationalGreaterThan, i2, i1});\n        assertThat(\"2 > 1 == true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testApply2OnOpWith3Inputs() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oChoice, bTrue, i1});\n        assertThat(\"result is an operator\", res1, instanceOf(ValueTypeOperator.ValueOperator.class));\n        assertThat(\"apply_2(?, true, 1)\", ((ValueTypeOperator.ValueOperator) res1).getRawValue().getInputTypes().length, is(1));\n        assertThat(\"apply_2(?, true, 1)\", ((ValueTypeOperator.ValueOperator) res1).getRawValue().getRequiredInputLength(), is(1));\n        assertThat(\"apply_2(?, true, 1)\", ((ValueTypeOperator.ValueOperator) res1).getRawValue().getInputTypes()[0], is(ValueTypes.CATEGORY_ANY));\n        assertThat(\"apply_2(?, true, 1)\", ((ValueTypeOperator.ValueOperator) res1).getRawValue().getOutputType(), is(ValueTypes.CATEGORY_ANY));\n\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{new Variable<>(res1), i2});\n        assertThat(\"result is an integer\", res2, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"apply_2(?, true, 1)(2)\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(1));\n    }\n\n    @Test\n    public void testInvalidInputSizeApply2Large() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oLogicalAnd, bFalse, bFalse, bFalse}));\n    }\n\n    @Test\n    public void testInvalidInputSizeApply2Small() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oLogicalAnd, bTrue}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypeApply2() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{bFalse, bFalse, bFalse}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeApply2() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oLogicalNot, oGeneralIdentity, bTrue}));\n    }\n\n    @Test\n    public void testValidateTypesApply2() {\n        assertThat(Operators.OPERATOR_APPLY_2.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_2.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_2.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_2.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesApply2() {\n        assertThat(Operators.OPERATOR_APPLY_2.getConditionalOutputType(new IVariable[]{oLogicalAnd, bFalse, bFalse}),\n                CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n        assertThat(Operators.OPERATOR_APPLY_2.getConditionalOutputType(new IVariable[]{oArithmeticModulus, i0, i0}),\n                CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n\n        assertThat(Operators.OPERATOR_APPLY_2.getConditionalOutputType(new IVariable[]{oRelationalEquals, bFalse, bFalse}),\n                CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n    }\n\n    /**\n     * ----------------------------------- APPLY_3 -----------------------------------\n     */\n\n    @Test\n    public void testApply3() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_APPLY_3.evaluate(new IVariable[]{oSubStr, i0, i1, sAnd});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"apply_3(substr, 0, 1, 'operator...') == 'o''\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"i\"));\n    }\n\n    @Test\n    public void testConditionalOutputTypesApply3() {\n        assertThat(Operators.OPERATOR_APPLY_3.getConditionalOutputType(new IVariable[]{oSubStr, i0, i1, sAnd}),\n                CoreMatchers.<IValueType>is(ValueTypes.STRING));\n    }\n\n    @Test\n    public void testValidateTypesApply3() {\n        assertThat(Operators.OPERATOR_APPLY_3.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_3.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_3.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_3.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_3.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY}), nullValue());\n    }\n\n    @Test\n    public void testApply3ThreeAnd() throws EvaluationException {\n        DummyVariableOperator oPipeFlip = new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oPipe}));\n        DummyVariableOperator oThreeAnd_1 = new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oPipeFlip, oLogicalAnd}));\n        DummyVariableOperator oThreeAnd = new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_APPLY_2.evaluate(new IVariable[]{oPipeFlip, oThreeAnd_1, oLogicalAnd}));\n\n        IValue res1 = Operators.OPERATOR_APPLY_3.evaluate(new IVariable[]{oThreeAnd, bTrue, bTrue, bTrue});\n        assertThat(\"result is a boolean\", res1.getType(), is(ValueTypes.BOOLEAN));\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"apply_3(&& &&, true, true, true)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        assertThat(Operators.OPERATOR_APPLY_3.getConditionalOutputType(new IVariable[]{oThreeAnd, bTrue, bTrue, bTrue}), is(ValueTypes.BOOLEAN));\n    }\n\n    /**\n     * ----------------------------------- APPLY_N -----------------------------------\n     */\n\n    @Test\n    public void testApplyN() throws EvaluationException {\n        DummyVariableList applyArgs = new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), sAnd.getValue()));\n        IValue res1 = Operators.OPERATOR_APPLY_N.evaluate(new IVariable[]{oSubStr, applyArgs});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"apply_n(substr, [0, 1, 'operator...']) == 'o''\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"i\"));\n    }\n\n    @Test\n    public void testConditionalOutputTypesApplyN() {\n        DummyVariableList applyArgs = new DummyVariableList(ValueTypeList.ValueList.ofAll(i0.getValue(), i1.getValue(), sAnd.getValue()));\n        assertThat(Operators.OPERATOR_APPLY_N.getConditionalOutputType(new IVariable[]{oSubStr, applyArgs}),\n                CoreMatchers.<IValueType>is(ValueTypes.STRING));\n    }\n\n    @Test\n    public void testValidateTypesApplyN() {\n        assertThat(Operators.OPERATOR_APPLY_N.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_N.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_N.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY}), nullValue());\n        assertThat(Operators.OPERATOR_APPLY_N.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY, ValueTypes.CATEGORY_ANY}), notNullValue());\n    }\n\n    /**\n     * ----------------------------------- APPLY_0 -----------------------------------\n     */\n\n    @Test\n    public void testApply0() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_APPLY_0.evaluate(new IVariable[]{oGetConstStr});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"apply_0(getConstStr) == 'HI'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"HI\"));\n    }\n\n    @Test\n    public void testConditionalOutputTypesApply0() {\n        assertThat(Operators.OPERATOR_APPLY_0.getConditionalOutputType(new IVariable[]{oGetConstStr}),\n                CoreMatchers.<IValueType>is(ValueTypes.STRING));\n    }\n\n    @Test\n    public void testValidateTypesApply0() {\n        assertThat(Operators.OPERATOR_APPLY_0.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_APPLY_0.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), nullValue());\n        assertThat(Operators.OPERATOR_APPLY_0.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY}), notNullValue());\n    }\n\n    /**\n     * ----------------------------------- MAP -----------------------------------\n     */\n\n    @Test\n    public void testMap() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_MAP.evaluate(new IVariable[]{oArithmeticIncrement, lintegers});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy list1 = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"map([0, 1, 2, 3], ++)[0] == 1\", ((ValueTypeInteger.ValueInteger) list1.get(0)).getRawValue(), is(1));\n        assertThat(\"map([0, 1, 2, 3], ++)[0] == 2\", ((ValueTypeInteger.ValueInteger) list1.get(1)).getRawValue(), is(2));\n        assertThat(\"map([0, 1, 2, 3], ++)[0] == 3\", ((ValueTypeInteger.ValueInteger) list1.get(2)).getRawValue(), is(3));\n        assertThat(\"map([0, 1, 2, 3], ++)[0] == 4\", ((ValueTypeInteger.ValueInteger) list1.get(3)).getRawValue(), is(4));\n        assertThat(list1.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.CATEGORY_NUMBER));\n\n        IValue res2 = Operators.OPERATOR_MAP.evaluate(new IVariable[]{oLogicalNot, lbooleans});\n        IValueTypeListProxy list2 = ((ValueTypeList.ValueList) res2).getRawValue();\n        assertThat(\"map([false, true, false, true], !)[0] == true\", ((ValueTypeBoolean.ValueBoolean) list2.get(0)).getRawValue(), is(true));\n        assertThat(\"map([false, true, false, true], !)[1] == false\", ((ValueTypeBoolean.ValueBoolean) list2.get(1)).getRawValue(), is(false));\n        assertThat(\"map([false, true, false, true], !)[2] == true\", ((ValueTypeBoolean.ValueBoolean) list2.get(2)).getRawValue(), is(true));\n        assertThat(\"map([false, true, false, true], !)[3] == false\", ((ValueTypeBoolean.ValueBoolean) list2.get(3)).getRawValue(), is(false));\n        assertThat(list2.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n\n        DummyVariableOperator curriedOperatorValue = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bTrue}));\n        IValue res3 = Operators.OPERATOR_MAP.evaluate(new IVariable[]{curriedOperatorValue, lbooleans});\n        IValueTypeListProxy list3 = ((ValueTypeList.ValueList) res3).getRawValue();\n        assertThat(\"map([false, true, false, true], true&&)[0] == false\", ((ValueTypeBoolean.ValueBoolean) list3.get(0)).getRawValue(), is(false));\n        assertThat(\"map([false, true, false, true], true&&)[1] == true\", ((ValueTypeBoolean.ValueBoolean) list3.get(1)).getRawValue(), is(true));\n        assertThat(\"map([false, true, false, true], true&&)[2] == false\", ((ValueTypeBoolean.ValueBoolean) list3.get(2)).getRawValue(), is(false));\n        assertThat(\"map([false, true, false, true], true&&)[3] == true\", ((ValueTypeBoolean.ValueBoolean) list3.get(3)).getRawValue(), is(true));\n        assertThat(list3.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.BOOLEAN));\n\n        IValue res4 = Operators.OPERATOR_MAP.evaluate(new IVariable[]{oLogicalAnd, lbooleans});\n        IValueTypeListProxy list4 = ((ValueTypeList.ValueList) res4).getRawValue();\n        assertThat(\"map([false, true, false, true], &&)[0] is an operator\", list4.get(0).getType(), is(ValueTypes.OPERATOR));\n        assertThat(\"map([false, true, false, true], &&)[1] is an operator\", list4.get(1).getType(), is(ValueTypes.OPERATOR));\n        assertThat(\"map([false, true, false, true], &&)[2] is an operator\", list4.get(2).getType(), is(ValueTypes.OPERATOR));\n        assertThat(\"map([false, true, false, true], &&)[3] is an operator\", list4.get(3).getType(), is(ValueTypes.OPERATOR));\n        assertThat(\"map([false, true, false, true], &&)[0](true) == false\",\n                ((ValueTypeBoolean.ValueBoolean) (((ValueTypeOperator.ValueOperator) list4.get(0)).getRawValue().evaluate(bTrue))).getRawValue(), is(false));\n        assertThat(\"map([false, true, false, true], &&)[1](true) == true\",\n                ((ValueTypeBoolean.ValueBoolean) (((ValueTypeOperator.ValueOperator) list4.get(1)).getRawValue().evaluate(bTrue))).getRawValue(), is(true));\n        assertThat(\"map([false, true, false, true], &&)[2](true) == false\",\n                ((ValueTypeBoolean.ValueBoolean) (((ValueTypeOperator.ValueOperator) list4.get(2)).getRawValue().evaluate(bTrue))).getRawValue(), is(false));\n        assertThat(\"map([false, true, false, true], &&)[3](true) == true\",\n                ((ValueTypeBoolean.ValueBoolean) (((ValueTypeOperator.ValueOperator) list4.get(3)).getRawValue().evaluate(bTrue))).getRawValue(), is(true));\n        assertThat(list4.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n    }\n\n    @Test\n    public void testInvalidInputSizeMapLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_MAP.evaluate(new IVariable[]{oArithmeticIncrement, lintegers, lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMapSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_MAP.evaluate(new IVariable[]{oArithmeticIncrement}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypeMap() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_MAP.evaluate(new IVariable[]{bFalse, bFalse}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeMap() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_MAP.evaluate(new IVariable[]{oArithmeticIncrement, oArithmeticIncrement}));\n    }\n\n    @Test\n    public void testValidateTypesMap() {\n        assertThat(Operators.OPERATOR_MAP.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_MAP.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_MAP.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_MAP.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.CATEGORY_ANY}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesMap() throws EvaluationException {\n        assertThat(Operators.OPERATOR_MAP.getConditionalOutputType(new IVariable[]{oArithmeticIncrement, lintegers}),\n                CoreMatchers.<IValueType>is(ValueTypes.LIST));\n        assertThat(Operators.OPERATOR_MAP.getConditionalOutputType(new IVariable[]{oLogicalNot, lbooleans}),\n                CoreMatchers.<IValueType>is(ValueTypes.LIST));\n\n        DummyVariableOperator curriedOperatorValue = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oLogicalAnd, bTrue}));\n        assertThat(Operators.OPERATOR_MAP.getConditionalOutputType(new IVariable[]{curriedOperatorValue, lbooleans}),\n                CoreMatchers.<IValueType>is(ValueTypes.LIST));\n    }\n\n    /**\n     * ----------------------------------- PREDICATE CONJUNCTION -----------------------------------\n     */\n\n    @Test\n    public void testPredicateConjunction() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        DummyVariableOperator twoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalGreaterThan, i2}));\n        DummyVariableOperator zeroLessThanandTwoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_CONJUNCTION.evaluate(new IVariable[]{zeroLessThan, twoGreaterThan}));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{zeroLessThanandTwoGreaterThan, i0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"0< && 2>(0) == false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{zeroLessThanandTwoGreaterThan, i1});\n        assertThat(\"0< && 2>(1) == true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{zeroLessThanandTwoGreaterThan, i2});\n        assertThat(\"0< && 2>(2) == false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateConjunctionLarge() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_CONJUNCTION.evaluate(new IVariable[]{zeroLessThan, zeroLessThan, zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateConjunctionSmall() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_CONJUNCTION.evaluate(new IVariable[]{zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypePredicateConjunction() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_CONJUNCTION.evaluate(new IVariable[]{bFalse, zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypePredicateConjunction() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_CONJUNCTION.evaluate(new IVariable[]{equalsTwo, i0}));\n    }\n\n    @Test\n    public void testValidateTypesPredicateConjunction() {\n        assertThat(Operators.OPERATOR_CONJUNCTION.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_CONJUNCTION.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_CONJUNCTION.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_CONJUNCTION.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesPredicateConjunction() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        assertThat(Operators.OPERATOR_CONJUNCTION.getConditionalOutputType(new IVariable[]{equalsTwo, equalsTwo}),\n                CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n    }\n\n    /**\n     * ----------------------------------- PREDICATE DISJUNCTION -----------------------------------\n     */\n\n    @Test\n    public void testPredicateDisjunction() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        DummyVariableOperator twoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalGreaterThan, i2}));\n        DummyVariableOperator zeroLessThanorTwoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_DISJUNCTION.evaluate(new IVariable[]{zeroLessThan, twoGreaterThan}));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{zeroLessThanorTwoGreaterThan, i0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"0< || 2>(0) == false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{zeroLessThanorTwoGreaterThan, i1});\n        assertThat(\"0< || 2>(1) == true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{zeroLessThanorTwoGreaterThan, i2});\n        assertThat(\"0< || 2>(2) == false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateDisjunctionLarge() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_DISJUNCTION.evaluate(new IVariable[]{zeroLessThan, zeroLessThan, zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateDisjunctionSmall() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_DISJUNCTION.evaluate(new IVariable[]{zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypePredicateDisjunction() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_DISJUNCTION.evaluate(new IVariable[]{bFalse, zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypePredicateDisjunction() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_DISJUNCTION.evaluate(new IVariable[]{equalsTwo, i0}));\n    }\n\n    @Test\n    public void testValidateTypesPredicateDisjunction() {\n        assertThat(Operators.OPERATOR_DISJUNCTION.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_DISJUNCTION.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_DISJUNCTION.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_DISJUNCTION.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesPredicateDisjunction() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        assertThat(Operators.OPERATOR_DISJUNCTION.getConditionalOutputType(new IVariable[]{equalsTwo, equalsTwo}),\n                CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n    }\n\n    /**\n     * ----------------------------------- PREDICATE NEGATION -----------------------------------\n     */\n\n    @Test\n    public void testPredicateNegation() throws EvaluationException {\n        DummyVariableOperator twoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalGreaterThan, i2}));\n        DummyVariableOperator notTwoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_NEGATION.evaluate(new IVariable[]{twoGreaterThan}));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{notTwoGreaterThan, i0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"!2>(0) == false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{notTwoGreaterThan, i1});\n        assertThat(\"!2>(1) == false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{notTwoGreaterThan, i2});\n        assertThat(\"!2>(2) == true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n        IValue res4 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{notTwoGreaterThan, i3});\n        assertThat(\"!2>(3) == true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateNegationLarge() throws EvaluationException {\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_NEGATION.evaluate(new IVariable[]{zeroLessThan, zeroLessThan}));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateNegationSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_NEGATION.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypePredicateNegation() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_NEGATION.evaluate(new IVariable[]{bFalse}));\n    }\n\n    @Test\n    public void testValidateTypesPredicateNegation() {\n        assertThat(Operators.OPERATOR_NEGATION.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_NEGATION.validateTypes(new IValueType[]{ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_NEGATION.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesPredicateNegation() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        assertThat(Operators.OPERATOR_NEGATION.getConditionalOutputType(new IVariable[]{equalsTwo}),\n                CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n    }\n\n    /**\n     * ----------------------------------- PREDICATE PIPE -----------------------------------\n     */\n\n    @Test\n    public void testPredicatePipe() throws EvaluationException {\n        DummyVariableOperator increment2 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticIncrement, oArithmeticIncrement}));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{increment2, i0});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"++ ++(0) == 2\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(2));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{increment2, i1});\n        assertThat(\"++ ++(1) == 3\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(3));\n    }\n\n    @Test\n    public void testPredicatePipeLargeInputCount() throws EvaluationException {\n        DummyVariableOperator incrementAndAdd = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oParseInt, oArithmeticAddition}));\n\n        assertThat(incrementAndAdd.getValue().getRawValue().getInputTypes().length, is(2));\n        assertThat(incrementAndAdd.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.STRING));\n        assertThat(incrementAndAdd.getValue().getRawValue().getInputTypes()[1], is(ValueTypes.CATEGORY_NUMBER));\n        assertThat(incrementAndAdd.getValue().getRawValue().getOutputType(), is(ValueTypes.CATEGORY_NUMBER));\n\n        IValue res1 = Operators.OPERATOR_APPLY_2.evaluate(incrementAndAdd, s1, i2);\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"int|+('1', 2) == 3\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(3));\n    }\n\n    @Test\n    public void testPredicatePipeMixed() throws EvaluationException {\n        DummyVariableOperator listLengthIncr = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oListLength, oArithmeticAddition}));\n\n        assertThat(listLengthIncr.getValue().getRawValue().getInputTypes().length, is(2));\n        assertThat(listLengthIncr.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.LIST));\n        assertThat(listLengthIncr.getValue().getRawValue().getInputTypes()[1], is(ValueTypes.CATEGORY_NUMBER));\n        assertThat(listLengthIncr.getValue().getRawValue().getOutputType(), is(ValueTypes.CATEGORY_NUMBER));\n\n        IValue res1 = Operators.OPERATOR_APPLY_2.evaluate(listLengthIncr, lintegers, i2);\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"len|+([0, 1, 2, 3],2) == 6\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(6));\n    }\n\n    @Test\n    public void testPredicatePipeLenAddMap() throws EvaluationException {\n        DummyVariableOperator op = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oListLength, new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                        Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticAddition, oOperatorMap}))}));\n\n        assertThat(op.getValue().getRawValue().getInputTypes().length, is(2));\n        assertThat(op.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.LIST));\n        assertThat(op.getValue().getRawValue().getInputTypes()[1], is(ValueTypes.LIST));\n        assertThat(op.getValue().getRawValue().getOutputType(), is(ValueTypes.LIST));\n\n        IValue res1 = Operators.OPERATOR_APPLY_2.evaluate(op, lintegers, lintegers);\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"len|+|map([0, 1, 2, 3],[0, 1, 2, 3]) == [4, 5, 6, 7]\",\n                res1, equalTo(ValueTypeList.ValueList.ofAll(\n                        ValueTypeInteger.ValueInteger.of(4),\n                        ValueTypeInteger.ValueInteger.of(5),\n                        ValueTypeInteger.ValueInteger.of(6),\n                        ValueTypeInteger.ValueInteger.of(7)\n                )));\n    }\n\n    @Test\n    public void testPredicatePipe3Incr() throws EvaluationException {\n        DummyVariableOperator op = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticIncrement, new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                        Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticIncrement, oArithmeticIncrement}))}));\n\n        assertThat(op.getValue().getRawValue().getInputTypes().length, is(1));\n        assertThat(op.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.CATEGORY_NUMBER));\n        assertThat(op.getValue().getRawValue().getOutputType(), is(ValueTypes.CATEGORY_NUMBER));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(op, i2);\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"++|++|++(2) == 5\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(5));\n    }\n\n    @Test\n    public void testPredicatePipeAddAddError() throws EvaluationException {\n        DummyVariableOperator op = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticAddition, oArithmeticAddition}));\n\n        assertThat(op.getValue().getRawValue().getInputTypes().length, is(2));\n        assertThat(op.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.CATEGORY_NUMBER));\n        assertThat(op.getValue().getRawValue().getInputTypes()[1], is(ValueTypes.CATEGORY_NUMBER));\n        assertThat(op.getValue().getRawValue().getOutputType(), is(ValueTypes.CATEGORY_NUMBER));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_APPLY.evaluate(op, i2, i2));\n    }\n\n    @Test\n    public void testPredicatePipeMapContains() throws EvaluationException {\n        DummyVariableOperator mapContains = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oListContains, oOperatorMap}));\n\n        assertThat(mapContains.getValue().getRawValue().getInputTypes().length, is(2));\n        assertThat(mapContains.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.LIST));\n        assertThat(mapContains.getValue().getRawValue().getInputTypes()[1], is(ValueTypes.LIST));\n        assertThat(mapContains.getValue().getRawValue().getOutputType(), is(ValueTypes.LIST));\n\n        IValue res1 = Operators.OPERATOR_APPLY_2.evaluate(mapContains, lintegers, lintegers);\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeList.ValueList.class));\n        assertThat(\"cont|map([0, 1, 2, 3],[0, 1, 2, 3]) == [true, true, true, true]\",\n                res1, equalTo(ValueTypeList.ValueList.ofAll(\n                        ValueTypeBoolean.ValueBoolean.of(true),\n                        ValueTypeBoolean.ValueBoolean.of(true),\n                        ValueTypeBoolean.ValueBoolean.of(true),\n                        ValueTypeBoolean.ValueBoolean.of(true)\n                )));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicatePipeLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticIncrement, oArithmeticIncrement, oArithmeticIncrement}));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicatePipeSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticIncrement}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypePredicatePipe() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_PIPE.evaluate(new IVariable[]{bFalse, oArithmeticIncrement}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypePredicatePipe() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_PIPE.evaluate(new IVariable[]{oArithmeticIncrement, i0}));\n    }\n\n    @Test\n    public void testValidateTypesPredicatePipe() {\n        assertThat(Operators.OPERATOR_PIPE.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_PIPE.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_PIPE.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_PIPE.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.OPERATOR}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesPredicatePipe() throws EvaluationException {\n        assertThat(Operators.OPERATOR_PIPE.getConditionalOutputType(new IVariable[]{oArithmeticIncrement, oArithmeticIncrement}),\n                CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n    }\n\n    /**\n     * ----------------------------------- PREDICATE PIPE 2 -----------------------------------\n     */\n\n    @Test\n    public void testPredicatePipe2() throws EvaluationException {\n        DummyVariableOperator addOneAndSelfMultiply = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE2.evaluate(new IVariable[]{oGeneralIdentity, oArithmeticIncrement, oArithmeticMultiplication}));\n\n        assertThat(addOneAndSelfMultiply.getValue().getRawValue().getInputTypes().length, is(1));\n        assertThat(addOneAndSelfMultiply.getValue().getRawValue().getInputTypes()[0], is(ValueTypes.CATEGORY_NUMBER));\n        assertThat(addOneAndSelfMultiply.getValue().getRawValue().getOutputType(), is(ValueTypes.CATEGORY_NUMBER));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{addOneAndSelfMultiply, i0});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\".2 id ++ * 0 == 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{addOneAndSelfMultiply, i1});\n        assertThat(\".2 id ++ * 1 == 2\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(2));\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{addOneAndSelfMultiply, i2});\n        assertThat(\".2 id ++ * 2 == 6\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(6));\n        IValue res4 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{addOneAndSelfMultiply, i3});\n        assertThat(\".2 id ++ * 3 == 12\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(12));\n    }\n\n    /**\n     * ----------------------------------- PREDICATE FLIP -----------------------------------\n     */\n\n    @Test\n    public void testPredicateFlip() throws EvaluationException {\n        DummyVariableOperator lessThanFlipped = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oRelationalLessThan}));\n        DummyVariableOperator lessThan2 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{lessThanFlipped, i2}));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{lessThan2, i0});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"<2(0) == true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{lessThan2, i1});\n        assertThat(\"<2(1) == true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n        IValue res3 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{lessThan2, i2});\n        assertThat(\"<2(2) == false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n        IValue res4 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{lessThan2, i3});\n        assertThat(\"<2(3) == false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testPredicateFlipOperatorWithThreeInputs() throws EvaluationException {\n        DummyVariableOperator choiceFlipped = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oChoice}));\n        DummyVariableOperator choiceFlipped0 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{choiceFlipped, i0}));\n        DummyVariableOperator choiceFlipped0False = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{choiceFlipped0, bFalse}));\n        DummyVariableOperator choiceFlipped0True = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{choiceFlipped0, bTrue}));\n\n        IValue res1 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{choiceFlipped0False, i1});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"flip choice 0 false 1 = 1\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n        IValue res2 = Operators.OPERATOR_APPLY.evaluate(new IVariable[]{choiceFlipped0True, i1});\n        assertThat(\"flip choice 0 true 1 = 0\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateFlipLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oRelationalLessThan, oRelationalLessThan}));\n    }\n\n    @Test\n    public void testInvalidInputSizePredicateFlipSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FLIP.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypePredicateFlip() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FLIP.evaluate(new IVariable[]{bFalse}));\n    }\n\n    @Test\n    public void testValidateTypesPredicateFlip() {\n        assertThat(Operators.OPERATOR_FLIP.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_FLIP.validateTypes(new IValueType[]{ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_FLIP.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesPredicateFlip() throws EvaluationException {\n        DummyVariableOperator lessThanFlipped = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oRelationalLessThan}));\n\n        assertThat(Operators.OPERATOR_FLIP.getConditionalOutputType(new IVariable[]{lessThanFlipped}),\n                CoreMatchers.<IValueType>is(ValueTypes.OPERATOR));\n    }\n\n    /**\n     * ----------------------------------- FILTER -----------------------------------\n     */\n\n    @Test\n    public void testFilter() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        // Filter: equal to 2\n        IValue res1 = Operators.OPERATOR_FILTER.evaluate(new IVariable[]{equalsTwo, lintegers});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy list1 = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"length(filter([0, 1, 2, 3], 2==)) == 1\", list1.getLength(), is(1));\n        assertThat(\"filter([0, 1, 2, 3], 2==)[0] == 2\", ((ValueTypeInteger.ValueInteger) list1.get(0)).getRawValue(), is(2));\n        assertThat(list1.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n\n        // Filter: greater than 0 and less than 2\n        DummyVariableOperator zeroLessThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalLessThan, i0}));\n        DummyVariableOperator twoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalGreaterThan, i2}));\n        DummyVariableOperator zeroLessThanandTwoGreaterThan = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_CONJUNCTION.evaluate(new IVariable[]{zeroLessThan, twoGreaterThan}));\n        IValue res2 = Operators.OPERATOR_FILTER.evaluate(new IVariable[]{zeroLessThanandTwoGreaterThan, lintegers});\n        IValueTypeListProxy list2 = ((ValueTypeList.ValueList) res2).getRawValue();\n        assertThat(\"length(<2 && >0([0, 1, 2, 3])) == 1\", list2.getLength(), is(1));\n        assertThat(\"<2 && >0([0, 1, 2, 3])[0] == 1\", ((ValueTypeInteger.ValueInteger) list2.get(0)).getRawValue(), is(1));\n        assertThat(list2.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n\n        // Filter: all even numbers\n        DummyVariableOperator modulusFlipped = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_FLIP.evaluate(new IVariable[]{oArithmeticModulus}));\n        DummyVariableOperator modulus2 = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{modulusFlipped, i2}));\n        DummyVariableOperator isZero = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i0}));\n        DummyVariableOperator isEvenUnsafe = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_PIPE.evaluate(new IVariable[]{modulus2, isZero}));\n        DummyVariableOperator isEven = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_DISJUNCTION.evaluate(new IVariable[]{isZero, isEvenUnsafe}));\n        IValue res3 = Operators.OPERATOR_FILTER.evaluate(new IVariable[]{isEven, lintegers});\n        IValueTypeListProxy list3 = ((ValueTypeList.ValueList) res3).getRawValue();\n        assertThat(\"length(even([0, 1, 2, 3])) == 2\", list3.getLength(), is(2));\n        assertThat(\"even([0, 1, 2, 3])[0] == 0\", ((ValueTypeInteger.ValueInteger) list3.get(0)).getRawValue(), is(0));\n        assertThat(\"even([0, 1, 2, 3])[1] == 2\", ((ValueTypeInteger.ValueInteger) list3.get(1)).getRawValue(), is(2));\n        assertThat(list2.getValueType(), CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n    }\n\n    @Test\n    public void testInvalidInputSizeFilterLarge() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FILTER.evaluate(new IVariable[]{equalsTwo, lintegers, lintegers}));\n    }\n\n    @Test\n    public void testInvalidInputSizeFilterSmall() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FILTER.evaluate(new IVariable[]{equalsTwo}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypeFilter() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FILTER.evaluate(new IVariable[]{bFalse, lintegers}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeFilter() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_FILTER.evaluate(new IVariable[]{equalsTwo, oArithmeticIncrement}));\n    }\n\n    @Test\n    public void testValidateTypesFilter() {\n        assertThat(Operators.OPERATOR_FILTER.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_FILTER.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_FILTER.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_FILTER.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesFilter() throws EvaluationException {\n        DummyVariableOperator equalsTwo = new DummyVariableOperator((ValueTypeOperator.ValueOperator)\n                Operators.OPERATOR_APPLY.evaluate(new IVariable[]{oRelationalEquals, i2}));\n\n        assertThat(Operators.OPERATOR_FILTER.getConditionalOutputType(new IVariable[]{equalsTwo, lintegers}),\n                CoreMatchers.<IValueType>is(ValueTypes.LIST));\n    }\n\n    /**\n     * ----------------------------------- REDUCE -----------------------------------\n     */\n\n    @Test\n    public void testReduce() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_REDUCE.evaluate(new IVariable[]{oArithmeticAddition, lintegers, i0});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"reduce([0, 1, 2, 3], +, 0) == 6\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(6));\n    }\n\n    @Test\n    public void testInvalidInputSizeReduceLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE.evaluate(new IVariable[]{oArithmeticAddition, lintegers, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeReduceSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE.evaluate(new IVariable[]{oArithmeticAddition, lintegers}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypeReduce() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE.evaluate(new IVariable[]{bFalse, lintegers, i0}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeReduce() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE.evaluate(new IVariable[]{oArithmeticAddition, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidOperatorAccumulatorTypeReduce() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE.evaluate(new IVariable[]{oArithmeticAddition, lintegers, lintegers}));\n    }\n\n    @Test\n    public void testValidateTypesReduce() {\n        assertThat(Operators.OPERATOR_REDUCE.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN, ValueTypes.INTEGER}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST, ValueTypes.INTEGER}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesReduce() throws EvaluationException {\n        assertThat(Operators.OPERATOR_REDUCE.getConditionalOutputType(new IVariable[]{oArithmeticAddition, lintegers, i0}),\n                CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n    }\n\n    /**\n     * ----------------------------------- REDUCE1 -----------------------------------\n     */\n\n    @Test\n    public void testReduce1() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{oArithmeticAddition, lintegers});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"reduce1([0, 1, 2, 3], +) == 6\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(6));\n    }\n\n    @Test\n    public void testReduce1Complex() throws EvaluationException {\n        // This is equivalent to `function reducer(a, b) { return choice(a > b, a, b) }`\n        DummyVariableOperator reducer = new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_PIPE2.evaluate(\n                new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.RELATIONAL_GT)),\n                new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_PIPE.evaluate(\n                        new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_FLIP.evaluate(new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.GENERAL_CHOICE)))),\n                        new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_FLIP))\n                )),\n                new DummyVariableOperator((ValueTypeOperator.ValueOperator) Operators.OPERATOR_APPLY.evaluate(\n                        new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.OPERATOR_PIPE2)),\n                        new DummyVariableOperator(ValueTypeOperator.ValueOperator.of(Operators.GENERAL_IDENTITY))\n                ))\n        ));\n\n\n        IValue res1 = Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{reducer, lintegers});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"reduce1(reducer, [0, 1, 2, 3]) == 3\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(3));\n    }\n\n    @Test\n    public void testInvalidInputSizeReduce1Large() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{oArithmeticAddition, lintegers, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeReduce1Small() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{oArithmeticAddition}));\n    }\n\n    @Test\n    public void testInvalidOperatorTypeReduce1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{bFalse, lintegers}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeReduce1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{oArithmeticAddition, i0}));\n    }\n\n    @Test\n    public void testInvalidEmptyListReduce1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_REDUCE1.evaluate(new IVariable[]{oArithmeticAddition, lempty}));\n    }\n\n    @Test\n    public void testValidateTypesReduce1() {\n        assertThat(Operators.OPERATOR_REDUCE1.validateTypes(new IValueType[]{}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE1.validateTypes(new IValueType[]{ValueTypes.OPERATOR}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE1.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE1.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.BOOLEAN, ValueTypes.INTEGER}), notNullValue());\n        assertThat(Operators.OPERATOR_REDUCE1.validateTypes(new IValueType[]{ValueTypes.OPERATOR, ValueTypes.LIST}), nullValue());\n    }\n\n    @Test\n    public void testConditionalOutputTypesReduce1() throws EvaluationException {\n        assertThat(Operators.OPERATOR_REDUCE1.getConditionalOutputType(new IVariable[]{oArithmeticAddition, lintegers}),\n                CoreMatchers.<IValueType>is(ValueTypes.INTEGER));\n    }\n\n    /**\n     * ----------------------------------- BY_NAME -----------------------------------\n     */\n\n    @Test\n    public void testByName() throws EvaluationException {\n        IValue res1 = Operators.OPERATOR_BY_NAME.evaluate(new IVariable[]{sAnd});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeOperator.ValueOperator.class));\n        assertThat(\"operator_by_name(and) == and\", ((ValueTypeOperator.ValueOperator) res1).getRawValue(), is(Operators.LOGICAL_AND));\n    }\n\n    @Test\n    public void testByNameNonExisting() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_BY_NAME.evaluate(new IVariable[]{sOther}));\n    }\n\n    @Test\n    public void testInvalidInputSizeByNameLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_BY_NAME.evaluate(new IVariable[]{sAnd, sAnd}));\n    }\n\n    @Test\n    public void testInvalidInputSizeByNameSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_BY_NAME.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidOperatorInputTypeByName() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.OPERATOR_BY_NAME.evaluate(new IVariable[]{oArithmeticIncrement}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestParseOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.ListTag;\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.cyclops.integrateddynamics.core.evaluate.operator.Operators.*;\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different parse operators.\n *\n * @author LostOfThought\n */\npublic class TestParseOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static IVariable[] s(String v) {\n        IVariable[] ret = new IVariable[1];\n        ret[0] = new DummyVariableString(ValueTypeString.ValueString.of(v));\n        return ret;\n    }\n\n    @BeforeEach\n    public void before() {\n\n    }\n\n    /**\n     * ----------------------------------- INTEGER -----------------------------------\n     */\n    @Test\n    public void testParseInt_IsInt() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"0\"));\n        assertThat(\"parse_Integer(\\\"0\\\") is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n    }\n\n    @Test\n    public void testParseIntEmpty() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_INTEGER.evaluate(s(\"\")));\n    }\n\n    @Test\n    public void testParseIntGarbage() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_INTEGER.evaluate(s(\"garbage\")));\n    }\n\n    @Test\n    public void testParseInt0() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"0\"));\n        assertThat(\"parse_Integer(0)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n    }\n\n    @Test\n    public void testParseInt1() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"1\"));\n        assertThat(\"parse_Integer(1)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n    }\n\n    @Test\n    public void testParseIntN1() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"-1\"));\n        assertThat(\"parse_Integer(-1)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(-1));\n    }\n\n    @Test\n    public void testParseIntP1() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"+1\"));\n        assertThat(\"parse_Integer(+1)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n    }\n\n    @Test\n    public void testParseIntHex_x() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"0xFF\"));\n        assertThat(\"parse_Integer(0xFF)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0xFF));\n    }\n\n    @Test\n    public void testParseIntHex_X() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"0XFF\"));\n        assertThat(\"parse_Integer(0XFF)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0xFF));\n    }\n\n    @Test\n    public void testParseIntHex_H() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"#FF\"));\n        assertThat(\"parse_Integer(#FF)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0xFF));\n    }\n\n    @Test\n    public void testParseIntNHex() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"-0xFF\"));\n        assertThat(\"parse_Integer(0xFF)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(-0xFF));\n    }\n\n    @Test\n    public void testParseIntOctal() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"01\"));\n        assertThat(\"parse_Integer(01)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(1));\n    }\n\n    @Test\n    public void testParseIntNOctal() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(\"-01\"));\n        assertThat(\"parse_Integer(-01)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(-1));\n    }\n\n    @Test\n    public void testParseIntMax() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(Long.toString((long) Integer.MAX_VALUE)));\n        assertThat(\"parse_Integer(<Integer.MAX_VALUE>)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(Integer.MAX_VALUE));\n    }\n\n    @Test\n    public void testParseIntMaxP1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_INTEGER.evaluate(s(Long.toString((long) Integer.MAX_VALUE + 1))));\n    }\n\n    @Test\n    public void testParseIntMin() throws EvaluationException {\n        IValue res1 = PARSE_INTEGER.evaluate(s(Long.toString((long) Integer.MIN_VALUE)));\n        assertThat(\"parse_Integer(<Integer.MIN_VALUE>)\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(Integer.MIN_VALUE));\n    }\n\n    @Test\n    public void testParseIntMinM1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_INTEGER.evaluate(s(Long.toString((long) Integer.MIN_VALUE - 1))));\n    }\n\n    /**\n     * ----------------------------------- LONG -----------------------------------\n     */\n\n    @Test\n    public void testParseLong_IsLong() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"0\"));\n        assertThat(\"parse_Integer(\\\"0\\\") is an integer\", res1, instanceOf(ValueTypeLong.ValueLong.class));\n    }\n\n    @Test\n    public void testParseLongEmpty() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_LONG.evaluate(s(\"\")));\n    }\n\n    @Test\n    public void testParseLongGarbage() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_LONG.evaluate(s(\"garbage\")));\n    }\n\n    @Test\n    public void testParseLong0() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"0\"));\n        assertThat(\"parse_Long(0)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0L));\n    }\n\n    @Test\n    public void testParseLong1() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"1\"));\n        assertThat(\"parse_Long(1)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(1L));\n    }\n\n    @Test\n    public void testParseLongN1() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"-1\"));\n        assertThat(\"parse_Long(-1)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(-1L));\n    }\n\n    @Test\n    public void testParseLongP1() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"+1\"));\n        assertThat(\"parse_Long(+1)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(1L));\n    }\n\n    @Test\n    public void testParseLongHex_x() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"0xFF\"));\n        assertThat(\"parse_Long(0xFF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0xFFL));\n    }\n\n    @Test\n    public void testParseLongHex_X() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"0XFF\"));\n        assertThat(\"parse_Long(0XFF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0xFFL));\n    }\n\n    @Test\n    public void testParseLongHex_H() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"#FF\"));\n        assertThat(\"parse_Long(#FF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0xFFL));\n    }\n\n    @Test\n    public void testParseLongNHex() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"-0xFF\"));\n        assertThat(\"parse_Long(-0xFF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(-0xFFL));\n    }\n\n    @Test\n    public void testParseLongNHex_X() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"-0XFF\"));\n        assertThat(\"parse_Long(-0XFF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(-0xFFL));\n    }\n\n    @Test\n    public void testParseLongNHex_H() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"-#FF\"));\n        assertThat(\"parse_Long(-#FF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(-0xFFL));\n    }\n\n    @Test\n    public void testParseLongPHex() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"+0xFF\"));\n        assertThat(\"parse_Long(+0xFF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0xFFL));\n    }\n\n    @Test\n    public void testParseLongPHex_X() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"+0XFF\"));\n        assertThat(\"parse_Long(+0XFF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0xFFL));\n    }\n\n    @Test\n    public void testParseLongPHex_H() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"+#FF\"));\n        assertThat(\"parse_Long(+#FF)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(0xFFL));\n    }\n\n    @Test\n    public void testParseLongOctal() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"01\"));\n        assertThat(\"parse_Long(01)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(1L));\n    }\n\n    @Test\n    public void testParseLongNOctal() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"-01\"));\n        assertThat(\"parse_Long(-01)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(-1L));\n    }\n\n    @Test\n    public void testParseLongPOctal() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(\"+01\"));\n        assertThat(\"parse_Long(+01)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(1L));\n    }\n\n    @Test\n    public void testParseLongMax() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(Long.toString(Long.MAX_VALUE)));\n        assertThat(\"parse_Long(<Long.MAX_VALUE>)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(Long.MAX_VALUE));\n    }\n\n    @Test\n    public void testParseLongMaxP1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_LONG.evaluate(s(\"9223372036854775808\")));\n    }\n\n    @Test\n    public void testParseLongMin() throws EvaluationException {\n        IValue res1 = PARSE_LONG.evaluate(s(Long.toString(Long.MIN_VALUE)));\n        assertThat(\"parse_Long(<Long.MIN_VALUE>)\", ((ValueTypeLong.ValueLong) res1).getRawValue(), is(Long.MIN_VALUE));\n    }\n\n    @Test\n    public void testParseLongMinM1() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_LONG.evaluate(s(\"-9223372036854775809\")));\n    }\n\n    /**\n     * ----------------------------------- DOUBLE -----------------------------------\n     */\n\n    @Test\n    public void testParseDouble_IsDouble() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"0.0\"));\n        assertThat(\"parse_Double(\\\"0.0\\\") is an double\", res1, instanceOf(ValueTypeDouble.ValueDouble.class));\n    }\n\n    @Test\n    public void testParseDoubleEmpty() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_DOUBLE.evaluate(s(\"\")));\n    }\n\n    @Test\n    public void testParseDoubleGarbage() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_DOUBLE.evaluate(s(\"garbage\")));\n    }\n\n    @Test\n    public void testParseDouble0() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"0\"));\n        assertThat(\"parse_Double(0)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(0.0));\n    }\n\n    @Test\n    public void testParseDouble1() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"1\"));\n        assertThat(\"parse_Double(1)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(1.0));\n    }\n\n    @Test\n    public void testParseDoubleN0() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-0.0\"));\n        assertThat(\"parse_Double(-0.0)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(-0.0));\n    }\n\n    @Test\n    public void testParseDoubleN1() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-1.0\"));\n        assertThat(\"parse_Double(-1.0)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(-1.0));\n    }\n\n    @Test\n    public void testParseDoubleHex_x() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"0xFF\"));\n        assertThat(\"parse_Double(0xFF)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(255.0));\n    }\n\n    @Test\n    public void testParseDoubleHex_X() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"0XFF\"));\n        assertThat(\"parse_Double(0XFF)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(255.0));\n    }\n\n    @Test\n    public void testParseDoubleHex_H() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"#FF\"));\n        assertThat(\"parse_Double(#FF)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(255.0));\n    }\n\n    @Test\n    public void testParseDoubleNHex() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-0xFF\"));\n        assertThat(\"parse_Double(-0xFF)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(-255.0));\n    }\n\n    @Test\n    public void testParseDoubleOctal() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"01\"));\n        assertThat(\"parse_Double(01)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(1.0));\n    }\n\n    @Test\n    public void testParseDoubleNOctal() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-01\"));\n        assertThat(\"parse_Double(-01)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(-1.0));\n    }\n\n    @Test\n    public void testParseDoubleDOctal() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"01.1\"));\n        assertThat(\"parse_Double(01)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(1.1));\n    }\n\n    @Test\n    public void testParseDoubleNDOctal() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-01.1\"));\n        assertThat(\"parse_Double(-01)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(-1.1));\n    }\n\n    @Test\n    public void testParseDoubleMax() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(Double.toString(Double.MAX_VALUE)));\n        assertThat(\"parse_Double(<Double.MAX_VALUE>)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.MAX_VALUE));\n    }\n\n    @Test\n    public void testParseDoubleMaxP1() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"1.7976931348623157e+309\"));\n        assertThat(\"parse_Double(<Double.MAX_VALUE * 10>)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleMin() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(Double.toString(Double.MIN_VALUE)));\n        assertThat(\"parse_Double(<Double.MIN_VALUE>)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.MIN_VALUE));\n    }\n\n    @Test\n    public void testParseDoubleMinM1() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-1.7976931348623157e+309\"));\n        assertThat(\"parse_Double(\\\"-1.7976931348623157e+309\\\")\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.NEGATIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleInf() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"Inf\"));\n        assertThat(\"parse_Double(Inf)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoublePInf() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"+Inf\"));\n        assertThat(\"parse_Double(+Inf)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleNInf() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-Inf\"));\n        assertThat(\"parse_Double(-Inf)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.NEGATIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleInfinity() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"Infinity\"));\n        assertThat(\"parse_Double(Infinity)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoublePInfinity() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"+Infinity\"));\n        assertThat(\"parse_Double(+Infinity)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleNInfinity() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-Infinity\"));\n        assertThat(\"parse_Double(-Infinity)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.NEGATIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleinf() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"inf\"));\n        assertThat(\"parse_Double(inf)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoublePinf() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"+inf\"));\n        assertThat(\"parse_Double(+inf)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleNinf() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-inf\"));\n        assertThat(\"parse_Double(-inf)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.NEGATIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleinfinity() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"infinity\"));\n        assertThat(\"parse_Double(infinity)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoublePinfinity() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"+infinity\"));\n        assertThat(\"parse_Double(+infinity)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleNinfinity() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-infinity\"));\n        assertThat(\"parse_Double(-infinity)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.NEGATIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleInfSym() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"\\u221E\"));\n        assertThat(\"parse_Double(infinitysym)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoublePInfSym() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"+\\u221E\"));\n        assertThat(\"parse_Double(+infinitysym)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.POSITIVE_INFINITY));\n    }\n\n    @Test\n    public void testParseDoubleNInfSym() throws EvaluationException {\n        IValue res1 = PARSE_DOUBLE.evaluate(s(\"-\\u221E\"));\n        assertThat(\"parse_Double(-infinitysym)\", ((ValueTypeDouble.ValueDouble) res1).getRawValue(), is(Double.NEGATIVE_INFINITY));\n    }\n\n    /**\n     * ----------------------------------- BOOLEAN -----------------------------------\n     */\n    @Test\n    public void testParseBoolean_IsBoolean() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"T\"));\n        assertThat(\"parse_Boolean(\\\"T\\\") is Boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n    }\n\n    @Test\n    public void testParseBooleanEmpty() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"\"));\n        assertThat(\"parse_Boolean(\\\"\\\")\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testParseBooleanNotEmpty() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"garbage\"));\n        assertThat(\"parse_Boolean(\\\"garbage\\\")\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBoolean0() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"0\"));\n        assertThat(\"parse_Boolean(0xFF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testParseBoolean1() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"1\"));\n        assertThat(\"parse_Boolean(0xFF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanN1() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"-1\"));\n        assertThat(\"parse_Boolean(0xFF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanHex_x() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"0xFF\"));\n        assertThat(\"parse_Boolean(0xFF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanHex_X() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"0XFF\"));\n        assertThat(\"parse_Boolean(0XFF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanHex_H() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"#FF\"));\n        assertThat(\"parse_Boolean(#FF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanNHex() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"-0xFF\"));\n        assertThat(\"parse_Boolean(0xFF)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanOctal() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"01\"));\n        assertThat(\"parse_Boolean(01)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanNOctal() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"-01\"));\n        assertThat(\"parse_Boolean(-01)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanT() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"T\"));\n        assertThat(\"parse_Boolean(T)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleant() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"t\"));\n        assertThat(\"parse_Boolean(t)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanTrue() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"True\"));\n        assertThat(\"parse_Boolean(True)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleantrue() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"true\"));\n        assertThat(\"parse_Boolean(true)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testParseBooleanF() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"F\"));\n        assertThat(\"parse_Boolean(F)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testParseBooleanf() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"f\"));\n        assertThat(\"parse_Boolean(f)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testParseBooleanFalse() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"False\"));\n        assertThat(\"parse_Boolean(False)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testParseBooleanfalse() throws EvaluationException {\n        IValue res1 = PARSE_BOOLEAN.evaluate(s(\"false\"));\n        assertThat(\"parse_Boolean(false)\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n    }\n\n    /**\n     * ----------------------------------- NBT -----------------------------------\n     */\n    @Test\n    public void testNBTGarbage() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_NBT.evaluate(s(\"}garbage{\")));\n    }\n\n    @Test\n    public void testNBTEmpty() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> PARSE_NBT.evaluate(s(\"\")));\n    }\n\n    @Test\n    public void testNBTFurnace() throws EvaluationException {\n        String NBT = \"{CookTime:0,x:2005,BurnTime:0,y:56,ForgeCaps:{},z:-81,Items:[],id:\\\"minecraft:furnace\\\",CookTimeTotal:0,Lock:\\\"\\\"}\";\n        IValue res1 = PARSE_NBT.evaluate(s(NBT));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", ((CompoundTag) ((ValueTypeNbt.ValueNbt) res1).getRawValue().get()).getString(\"id\").get(), instanceOf(String.class));\n    }\n\n    @Test\n    public void testNBTFurnaceSpaces() throws EvaluationException {\n        String NBT = \"{\\rCookTime:\\n0,\\tx:2005, BurnTime:0,  y:56,ForgeCaps:{},z:-81,Items:[],id:\\r\\n\\t \\\"minecraft:furnace\\\",CookTimeTotal  :0,Lock:\\\"\\\"}\";\n        IValue res1 = PARSE_NBT.evaluate(s(NBT));\n        CompoundTag nbt = (CompoundTag) ((ValueTypeNbt.ValueNbt) res1).getRawValue().get();\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getInt(\"CookTime\").get(), is(0));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getInt(\"x\").get(), is(2005));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getInt(\"BurnTime\").get(), is(0));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getInt(\"y\").get(), is(56));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getCompound(\"ForgeCaps\").get().isEmpty(), is(true));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getInt(\"z\").get(), is(-81));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.get(\"Items\"), instanceOf(ListTag.class));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getString(\"id\").get(), is(\"minecraft:furnace\"));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getInt(\"CookTimeTotal\").get(), is(0));\n        assertThat(\"parse_NBT(\\\"\" + NBT + \"\\\")\", nbt.getString(\"Lock\").get(), is(\"\"));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestRelationalOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different integer operators.\n *\n * @author rubensworks\n */\npublic class TestRelationalOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<DummyValueType.DummyValue>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger im10;\n    private DummyVariableInteger i10;\n\n    private DummyVariableDouble d0;\n    private DummyVariableDouble d1;\n    private DummyVariableDouble dm10;\n    private DummyVariableDouble d10;\n\n    @BeforeEach\n    public void before() {\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        im10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-10));\n        i10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10));\n\n        d0 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0.1));\n        d1 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(1.1));\n        dm10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(-10.1));\n        d10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(10.1));\n    }\n\n    /**\n     * ----------------------------------- EQUALS -----------------------------------\n     */\n\n    @Test\n    public void testRelationalEquals() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 == 10 = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 == 10 = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 == 0 = false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 == 10 = false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n\n        IValue res5 = Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 == -10 = false\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEqualsSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_EQUALS.evaluate(new IVariable[]{i0}));\n    }\n\n    /**\n     * ----------------------------------- GT -----------------------------------\n     */\n\n    @Test\n    public void testRelationalGT() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_GT.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 > 10 = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.RELATIONAL_GT.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 > 10 = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.RELATIONAL_GT.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 > 0 = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.RELATIONAL_GT.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 > 10 = false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n\n        IValue res5 = Operators.RELATIONAL_GT.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 > -10 = true\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeGTLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_GT.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeGTSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_GT.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeGT() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_GT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- LT -----------------------------------\n     */\n\n    @Test\n    public void testRelationalLT() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_LT.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 < 10 = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.RELATIONAL_LT.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 < 10 = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.RELATIONAL_LT.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 < 0 = false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.RELATIONAL_LT.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 < 10 = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n\n        IValue res5 = Operators.RELATIONAL_LT.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 < -10 = false\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeLTLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_LT.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLTSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_LT.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeLT() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_LT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- NOT EQUALS -----------------------------------\n     */\n\n    @Test\n    public void testRelationalNotEquals() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 != 10 = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(false));\n\n        IValue res2 = Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 != 10 = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 != 0 = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 != 10 = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n\n        IValue res5 = Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 != -10 = true\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeNotEqualsLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeNotEqualsSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_NOTEQUALS.evaluate(new IVariable[]{i0}));\n    }\n\n    /**\n     * ----------------------------------- GE -----------------------------------\n     */\n\n    @Test\n    public void testRelationalGE() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 >= 10 = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 >= 10 = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 >= 0 = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 >= 10 = false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n\n        IValue res5 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 >= -10 = true\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testRelationalGEDouble() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{d10, d10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 >= 10 = true\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{d0, d10});\n        assertThat(\"0 >= 10 = false\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n\n        IValue res3 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{d10, d0});\n        assertThat(\"10 >= 0 = true\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n\n        IValue res4 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{dm10, d10});\n        assertThat(\"-10 >= 10 = false\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n\n        IValue res5 = Operators.RELATIONAL_GE.evaluate(new IVariable[]{d10, dm10});\n        assertThat(\"10 >= -10 = true\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidInputSizeGELarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_GE.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeGESmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_GE.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeGE() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_GE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- LE -----------------------------------\n     */\n\n    @Test\n    public void testRelationalLE() throws EvaluationException {\n        IValue res1 = Operators.RELATIONAL_LE.evaluate(new IVariable[]{i10, i10});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"10 <= 10 = false\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n\n        IValue res2 = Operators.RELATIONAL_LE.evaluate(new IVariable[]{i0, i10});\n        assertThat(\"0 <= 10 = true\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n\n        IValue res3 = Operators.RELATIONAL_LE.evaluate(new IVariable[]{i10, i0});\n        assertThat(\"10 <= 0 = false\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n\n        IValue res4 = Operators.RELATIONAL_LE.evaluate(new IVariable[]{im10, i10});\n        assertThat(\"-10 <= 10 = true\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(true));\n\n        IValue res5 = Operators.RELATIONAL_LE.evaluate(new IVariable[]{i10, im10});\n        assertThat(\"10 <= -10 = false\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeLELarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_LE.evaluate(new IVariable[]{i0, i0, i0}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLESmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_LE.evaluate(new IVariable[]{i0}));\n    }\n\n    @Test\n    public void testInvalidInputTypeLE() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.RELATIONAL_LE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestStringOperators.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.Identifier;\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValue;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Iterator;\n\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different integer operators.\n *\n * @author rubensworks\n */\npublic class TestStringOperators {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    private static final DummyValueType DUMMY_TYPE = DummyValueType.TYPE;\n    private static final DummyVariable<DummyValueType.DummyValue> DUMMY_VARIABLE =\n            new DummyVariable<>(DUMMY_TYPE, DummyValueType.DummyValue.of());\n\n    private DummyVariableString sempty;\n    private DummyVariableString sabc;\n    private DummyVariableString sl;\n    private DummyVariableString scomma;\n    private DummyVariableString shello;\n    private DummyVariableString sworld;\n    private DummyVariableString shelloWorld;\n    private DummyVariableString sregex;\n    private DummyVariableString sbrokenRegex;\n    private DummyVariableInteger i0;\n    private DummyVariableInteger i1;\n    private DummyVariableInteger i2;\n    private DummyVariableInteger i10;\n    private DummyVariableInteger i11;\n    private DummyVariableDouble d10_5;\n    private DummyVariableList labc;\n    private DummyVariableList lint;\n    private DummyVariableList lstringinvalidtypes;\n    private DummyVariableList lstring_inf;\n\n    @BeforeAll\n    public static void beforeClass() {\n        ValueTypeListProxyFactories.load();\n    }\n\n    @BeforeEach\n    public void before() {\n        sempty = new DummyVariableString(ValueTypeString.ValueString.of(\"\"));\n        sabc = new DummyVariableString(ValueTypeString.ValueString.of(\"abc\"));\n        sl = new DummyVariableString(ValueTypeString.ValueString.of(\"l\"));\n        scomma = new DummyVariableString(ValueTypeString.ValueString.of(\",\"));\n        shello = new DummyVariableString(ValueTypeString.ValueString.of(\"hello\"));\n        sworld = new DummyVariableString(ValueTypeString.ValueString.of(\"world\"));\n        shelloWorld = new DummyVariableString(ValueTypeString.ValueString.of(\"hello world\"));\n        sregex = new DummyVariableString(ValueTypeString.ValueString.of(\"\\\\A(.+?)(world)\\\\z\"));\n        sbrokenRegex = new DummyVariableString(ValueTypeString.ValueString.of(\"[.+\"));\n        i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        i1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(1));\n        i2 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(2));\n        i10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10));\n        i11 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(11));\n        d10_5 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(10.5D));\n        labc = new DummyVariableList(ValueTypeList.ValueList.ofAll(ValueTypes.STRING,\n                ValueTypeString.ValueString.of(\"a\"),\n                ValueTypeString.ValueString.of(\"b\"),\n                ValueTypeString.ValueString.of(\"c\")\n        ));\n        lint = new DummyVariableList(ValueTypeList.ValueList.ofAll(ValueTypes.INTEGER,\n                ValueTypeInteger.ValueInteger.of(123)));\n        lstringinvalidtypes = new DummyVariableList(ValueTypeList.ValueList.ofAll(ValueTypes.STRING,\n                ValueTypeInteger.ValueInteger.of(123)\n        ));\n        lstring_inf = new DummyVariableList(ValueTypeList.ValueList.ofFactory(new IValueTypeListProxy() {\n            @Override\n            public int getLength() throws EvaluationException {\n                return 0;\n            }\n\n            @Override\n            public IValue get(int index) throws EvaluationException {\n                return null;\n            }\n\n            @Override\n            public IValueType<? extends IValue> getValueType() {\n                return ValueTypes.STRING;\n            }\n\n            @Override\n            public Identifier getName() {\n                return null;\n            }\n\n            @Override\n            public MutableComponent toCompactString() {\n                return null;\n            }\n\n            @Override\n            public boolean isInfinite() {\n                return true;\n            }\n\n            @Override\n            public Iterator<IValue> iterator() {\n                return null;\n            }\n        }));\n    }\n\n    /**\n     * ----------------------------------- LENGTH -----------------------------------\n     */\n\n    @Test\n    public void testStringLength() throws EvaluationException {\n        IValue res1 = Operators.STRING_LENGTH.evaluate(new IVariable[]{sabc});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"len(abc) = 3\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(3));\n    }\n\n    @Test\n    public void testInvalidInputSizeLengthLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_LENGTH.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeLengthSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_LENGTH.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeLength() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_LENGTH.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CONCAT -----------------------------------\n     */\n\n    @Test\n    public void testStringConcat() throws EvaluationException {\n        IValue res1 = Operators.STRING_CONCAT.evaluate(new IVariable[]{sabc, sabc});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"abc + abc = abcabc\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"abcabc\"));\n    }\n\n    @Test\n    public void testInvalidInputSizConcatLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONCAT.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeConcatSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONCAT.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeConcat() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONCAT.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CONTAINS -----------------------------------\n     */\n\n    @Test\n    public void testStringContains() throws EvaluationException {\n        IValue res1 = Operators.STRING_CONTAINS.evaluate(new IVariable[]{shello, shelloWorld});\n        IValue res2 = Operators.STRING_CONTAINS.evaluate(new IVariable[]{sworld, shelloWorld});\n        IValue res3 = Operators.STRING_CONTAINS.evaluate(new IVariable[]{sabc, shelloWorld});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"hello world contains hello\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        assertThat(\"hello world contains world\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n        assertThat(\"hello world doesn't contain abc\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeContains() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- INDEX_OF -----------------------------------\n     */\n\n    @Test\n    public void testStringIndexOf() throws EvaluationException {\n        IValue res1 = Operators.STRING_INDEX_OF.evaluate(new IVariable[]{shello, shelloWorld});\n        IValue res2 = Operators.STRING_INDEX_OF.evaluate(new IVariable[]{sworld, shelloWorld});\n        IValue res3 = Operators.STRING_INDEX_OF.evaluate(new IVariable[]{sabc, shelloWorld});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"hello world index_of hello = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n        assertThat(\"hello world index_of world = 6\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(6));\n        assertThat(\"hello world index_of abc = -1\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(-1));\n    }\n\n    @Test\n    public void testInvalidInputSizeIndexOfLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIndexOfSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeIndexOf() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- INDEX_OF_REGEX -----------------------------------\n     */\n\n    @Test\n    public void testStringIndexOfRegex() throws EvaluationException {\n        DummyVariableString word = new DummyVariableString(ValueTypeString.ValueString.of(\"worl?d\"));\n        IValue res1 = Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{sregex, shelloWorld});\n        IValue res2 = Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{sworld, shelloWorld});\n        IValue res3 = Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{word, shelloWorld});\n        IValue res4 = Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{sabc, shelloWorld});\n        assertThat(\"result is an integer\", res1, instanceOf(ValueTypeInteger.ValueInteger.class));\n        assertThat(\"hello world index_of_regex complex regex = 0\", ((ValueTypeInteger.ValueInteger) res1).getRawValue(), is(0));\n        assertThat(\"hello world index_of_regex world = 6\", ((ValueTypeInteger.ValueInteger) res2).getRawValue(), is(6));\n        assertThat(\"hello world index_of_regex worl?d = 6\", ((ValueTypeInteger.ValueInteger) res3).getRawValue(), is(6));\n        assertThat(\"hello world index_of_regex abc = -1\", ((ValueTypeInteger.ValueInteger) res4).getRawValue(), is(-1));\n    }\n\n    @Test\n    public void testInvalidPatternIndexOfRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{sbrokenRegex, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIndexOfRegexLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeIndexOfRegexSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeIndexOfRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_INDEX_OF_REGEX.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- STARTS_WITH -----------------------------------\n     */\n\n    @Test\n    public void testStringStartsWith() throws EvaluationException {\n        IValue res1 = Operators.STRING_STARTS_WITH.evaluate(new IVariable[]{shello, shelloWorld});\n        IValue res2 = Operators.STRING_STARTS_WITH.evaluate(new IVariable[]{sworld, shelloWorld});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"hello world starts with hello\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        assertThat(\"hello world doesn't start with world\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeStartsWithLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_STARTS_WITH.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeStartsWithSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_STARTS_WITH.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeStartsWith() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_STARTS_WITH.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- ENDS_WITH -----------------------------------\n     */\n\n    @Test\n    public void testStringEndsWith() throws EvaluationException {\n        IValue res1 = Operators.STRING_ENDS_WITH.evaluate(new IVariable[]{sworld, shelloWorld});\n        IValue res2 = Operators.STRING_ENDS_WITH.evaluate(new IVariable[]{shello, shelloWorld});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"hello world ends with world\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        assertThat(\"hello world doesn't end with hello\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidInputSizeEndsWithLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ENDS_WITH.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeEndsWithSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ENDS_WITH.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeEndsWith() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ENDS_WITH.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- CONTAINS_REGEX -----------------------------------\n     */\n\n    @Test\n    public void testStringContainsRegex() throws EvaluationException {\n        DummyVariableString shelloPlus = new DummyVariableString(ValueTypeString.ValueString.of(\"hello.+\"));\n        DummyVariableString sstarWorld = new DummyVariableString(ValueTypeString.ValueString.of(\".*world\"));\n        DummyVariableString sello = new DummyVariableString(ValueTypeString.ValueString.of(\"e..o\"));\n        IValue res1 = Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{shelloPlus, shelloWorld});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"hello world contains_regex hello.+\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        IValue res2 = Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sstarWorld, shelloWorld});\n        assertThat(\"hello world contains_regex .*world\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n        IValue res3 = Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sregex, shelloWorld});\n        assertThat(\"hello world contains a complex regex\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n        IValue res4 = Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sabc, shelloWorld});\n        assertThat(\"hello world doesn't match abc\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n        IValue res5 = Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sello, shelloWorld});\n        assertThat(\"hello world contains e..o\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(true));\n    }\n\n    @Test\n    public void testInvalidPatternContainsRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sbrokenRegex, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsRegexLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeContainsRegexSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeContainsRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_CONTAINS_REGEX.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- MATCHES_REGEX -----------------------------------\n     */\n\n    @Test\n    public void testStringMatchesRegex() throws EvaluationException {\n        DummyVariableString shelloPlus = new DummyVariableString(ValueTypeString.ValueString.of(\"hello.+\"));\n        DummyVariableString sstarWorld = new DummyVariableString(ValueTypeString.ValueString.of(\".*world\"));\n        DummyVariableString sello = new DummyVariableString(ValueTypeString.ValueString.of(\"e..o\"));\n        IValue res1 = Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{shelloPlus, shelloWorld});\n        assertThat(\"result is a boolean\", res1, instanceOf(ValueTypeBoolean.ValueBoolean.class));\n        assertThat(\"hello world matches_regex hello.+\", ((ValueTypeBoolean.ValueBoolean) res1).getRawValue(), is(true));\n        IValue res2 = Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sstarWorld, shelloWorld});\n        assertThat(\"hello world matches_regex .*world\", ((ValueTypeBoolean.ValueBoolean) res2).getRawValue(), is(true));\n        IValue res3 = Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sregex, shelloWorld});\n        assertThat(\"hello world matches a complex regex\", ((ValueTypeBoolean.ValueBoolean) res3).getRawValue(), is(true));\n        IValue res4 = Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sabc, shelloWorld});\n        assertThat(\"hello world doesn't match abc\", ((ValueTypeBoolean.ValueBoolean) res4).getRawValue(), is(false));\n        IValue res5 = Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sello, shelloWorld});\n        assertThat(\"hello world doesn't match e..o\", ((ValueTypeBoolean.ValueBoolean) res5).getRawValue(), is(false));\n    }\n\n    @Test\n    public void testInvalidPatternMatchesRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sbrokenRegex, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMatchesRegexLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeMatchesRegexSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeMatchesRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_MATCHES_REGEX.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- SPLIT_ON -----------------------------------\n     */\n\n    @Test\n    public void testStringSplitOn() throws EvaluationException {\n        DummyVariableString sspace = new DummyVariableString(ValueTypeString.ValueString.of(\" \"));\n        IValue res1 = Operators.STRING_SPLIT_ON.evaluate(new IVariable[]{sspace, shelloWorld});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"split_on('hello world', ' ')[0] = hello\", list.get(0).getRawValue(), is(\"hello\"));\n        assertThat(\"split_on('hello world', ' ')[1] = world\", list.get(1).getRawValue(), is(\"world\"));\n        assertThat(\"split_on('hello world', ' ').size = 2\", list.getLength(), is(2));\n    }\n\n    @Test\n    public void testInvalidInputSizeSplitOnLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSplitOnSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeSplitOn() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- SPLIT_ON_REGEX -----------------------------------\n     */\n\n    @Test\n    public void testStringSplitOnRegex() throws EvaluationException {\n        DummyVariableString swhitespace = new DummyVariableString(ValueTypeString.ValueString.of(\"\\\\s\"));\n        IValue res1 = Operators.STRING_SPLIT_ON_REGEX.evaluate(new IVariable[]{swhitespace, shelloWorld});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"split_on_regex('hello world', ' ')[0] = hello\", list.get(0).getRawValue(), is(\"hello\"));\n        assertThat(\"split_on_regex('hello world', ' ')[1] = world\", list.get(1).getRawValue(), is(\"world\"));\n        assertThat(\"split_on_regex('hello world', ' ').size = 2\", list.getLength(), is(2));\n    }\n\n    @Test\n    public void testInvalidPatternSplitOnRegex() throws EvaluationException {\n        DummyVariableString sbroken = new DummyVariableString(ValueTypeString.ValueString.of(\"[.+\"));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON_REGEX.evaluate(new IVariable[]{sbroken, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSplitOnRegexLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON_REGEX.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSplitOnRegexSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON_REGEX.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeSplitOnRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SPLIT_ON_REGEX.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- SUBSTRING -----------------------------------\n     */\n\n    @Test\n    public void testStringSubstring() throws EvaluationException {\n        IValue res1 = Operators.STRING_SUBSTRING.evaluate(new IVariable[]{i1, i2, sabc});\n        IValue res2 = Operators.STRING_SUBSTRING.evaluate(new IVariable[]{i1, i10, shelloWorld});\n        IValue res3 = Operators.STRING_SUBSTRING.evaluate(new IVariable[]{i1, i1, sabc});\n        IValue res4 = Operators.STRING_SUBSTRING.evaluate(new IVariable[]{i1, i11, shelloWorld});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"abc substring (1, 2) = b\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"b\"));\n        assertThat(\"hello world substring (1, 10) = 'ello worl'\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"ello worl\"));\n        assertThat(\"abc substring (1, 1) = ''\", ((ValueTypeString.ValueString) res3).getRawValue(), is(\"\"));\n        assertThat(\"hello world substring (1, 11) = 'ello world'\", ((ValueTypeString.ValueString) res4).getRawValue(), is(\"ello world\"));\n    }\n\n    @Test\n    public void testOutOfBoundsSubstring() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SUBSTRING.evaluate(new IVariable[]{i10, i10, sabc}));\n    }\n\n    @Test\n    public void testNegativeSubstring() throws EvaluationException {\n        DummyVariableInteger iNeg1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-1));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SUBSTRING.evaluate(new IVariable[]{iNeg1, i10, sabc}));\n    }\n\n    @Test\n    public void testInvertedSubstring() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SUBSTRING.evaluate(new IVariable[]{i10, i1, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSubstringLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SUBSTRING.evaluate(new IVariable[]{sabc, sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeSubstringSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SUBSTRING.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeSubstring() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_SUBSTRING.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- REGEX_GROUP -----------------------------------\n     */\n\n    @Test\n    public void testStringRegexGroup() throws EvaluationException {\n        IValue res1 = Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sregex, i0, shelloWorld});\n        IValue res2 = Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sregex, i1, shelloWorld});\n        IValue res3 = Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sregex, i2, shelloWorld});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"hello world regex_group ('\\\\A(.+?)(world)\\\\z', 0) = hello world\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"hello world\"));\n        assertThat(\"hello world regex_group ('\\\\A(.+?)(world)\\\\z', 1) = 'hello '\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"hello \"));\n        assertThat(\"hello world regex_group ('\\\\A(.+?)(world)\\\\z', 2) = world\", ((ValueTypeString.ValueString) res3).getRawValue(), is(\"world\"));\n    }\n\n    @Test\n    public void testOutOfBoundsRegexGroup() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sregex, i10, shelloWorld}));\n    }\n\n    @Test\n    public void testNegativeRegexGroup() throws EvaluationException {\n        DummyVariableInteger iNeg1 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-1));\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sregex, iNeg1, shelloWorld}));\n    }\n\n    @Test\n    public void testInvalidPatternRegexGroup() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sbrokenRegex, i1, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRegexGroupLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sabc, sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRegexGroupSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeRegexGroup() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUP.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- REGEX_GROUPS -----------------------------------\n     */\n\n    @Test\n    public void testStringRegexGroups() throws EvaluationException {\n        IValue res1 = Operators.STRING_REGEX_GROUPS.evaluate(new IVariable[]{sabc, shelloWorld});\n        IValue res2 = Operators.STRING_REGEX_GROUPS.evaluate(new IVariable[]{sregex, shelloWorld});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list1 = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"(hello_world regex_groups abc).size = 0\", list1.getLength(), is(0));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list2 = ((ValueTypeList.ValueList) res2).getRawValue();\n        assertThat(\"hello world regex_groups ('\\\\A(.+?)(world)\\\\z')[0] = hello world\", list2.get(0).getRawValue(), is(\"hello world\"));\n        assertThat(\"hello world regex_groups ('\\\\A(.+?)(world)\\\\z')[1] = 'hello '\", list2.get(1).getRawValue(), is(\"hello \"));\n        assertThat(\"hello world regex_groups ('\\\\A(.+?)(world)\\\\z')[2] = world\", list2.get(2).getRawValue(), is(\"world\"));\n        assertThat(\"(hello world regex_groups ('\\\\A(.+?)(world)\\\\z')).size = 3\", list2.getLength(), is(3));\n    }\n\n    @Test\n    public void testInvalidPatternRegexGroups() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUPS.evaluate(new IVariable[]{sbrokenRegex, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRegexGroupsLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUPS.evaluate(new IVariable[]{sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRegexGroupsSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUPS.evaluate(new IVariable[]{sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeRegexGroups() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_GROUPS.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- REGEX_SCAN -----------------------------------\n     */\n\n    @Test\n    public void testStringRegexScan() throws EvaluationException {\n        DummyVariableString firstLetters = new DummyVariableString(ValueTypeString.ValueString.of(\"(\\\\S)\\\\S*\"));\n        IValue res1 = Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{sabc, i0, shelloWorld});\n        IValue res2 = Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{firstLetters, i1, shelloWorld});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list1 = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"(hello_world regex_scan abc).size = 0\", list1.getLength(), is(0));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list2 = ((ValueTypeList.ValueList) res2).getRawValue();\n        assertThat(\"hello world regex_scan ('(\\\\S)\\\\S*', 1)[0] = h\", list2.get(0).getRawValue(), is(\"h\"));\n        assertThat(\"hello world regex_scan ('(\\\\S)\\\\S*', 1)[1] = w\", list2.get(1).getRawValue(), is(\"w\"));\n        assertThat(\"hello world regex_scan ('(\\\\S)\\\\S*', 1).size = 2\", list2.getLength(), is(2));\n    }\n\n    @Test\n    public void testStringRegexScanComplex() throws EvaluationException {\n        DummyVariableString regex = new DummyVariableString(ValueTypeString.ValueString.of(\"(?:.*?([^\\\\(\\\\s]*\\\\([^\\\\(]*?\\\\)).*?)?\"));\n        DummyVariableString haystack = new DummyVariableString(ValueTypeString.ValueString.of(\"test1(test2(test3, test4), test5(test6, test7))\"));\n        IValue res1 = Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{regex, i1, haystack});\n        assertThat(\"result is a list\", res1, instanceOf(ValueTypeList.ValueList.class));\n        IValueTypeListProxy<ValueTypeString, ValueTypeString.ValueString> list1 = ((ValueTypeList.ValueList) res1).getRawValue();\n        assertThat(\"(...).size = 2\", list1.getLength(), is(2));\n        assertThat(\"...[0] = ...\", list1.get(0).getRawValue(), is(\"test2(test3, test4)\"));\n        assertThat(\"...[1] = ...\", list1.get(1).getRawValue(), is(\"test5(test6, test7)\"));\n    }\n\n    @Test\n    public void testInvalidPatternRegexScan() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{sbrokenRegex, i0, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRegexScanLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{sabc, sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeRegexScanSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeRegexScan() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REGEX_SCAN.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- REPLACE -----------------------------------\n     */\n\n    @Test\n    public void testStringReplace() throws EvaluationException {\n        IValue res1 = Operators.STRING_REPLACE.evaluate(new IVariable[]{sl, sempty, shelloWorld});\n        IValue res2 = Operators.STRING_REPLACE.evaluate(new IVariable[]{shelloWorld, sempty, shelloWorld});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"hello world replace (l, '') = heo word\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"heo word\"));\n        assertThat(\"hello world replace (hello world, '') = ''\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"\"));\n    }\n\n    @Test\n    public void testInvalidInputSizeReplaceLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE.evaluate(new IVariable[]{sabc, sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeReplaceSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeReplace() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- REPLACE_REGEX -----------------------------------\n     */\n\n    @Test\n    public void testStringReplaceRegex() throws EvaluationException {\n        DummyVariableString szerozero = new DummyVariableString(ValueTypeString.ValueString.of(\"$0$0\"));\n        DummyVariableString sone = new DummyVariableString(ValueTypeString.ValueString.of(\"$1\"));\n        IValue res1 = Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{sl, szerozero, shelloWorld});\n        IValue res2 = Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{sregex, sone, shelloWorld});\n        IValue res3 = Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{sregex, sempty, shelloWorld});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"hello world replace (l, '$0') = 'hellllo worlld'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"hellllo worlld\"));\n        assertThat(\"hello world replace (complex regex, $1) = 'hello '\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"hello \"));\n        assertThat(\"hello world replace (complex regex, '') = ''\", ((ValueTypeString.ValueString) res3).getRawValue(), is(\"\"));\n    }\n\n    @Test\n    public void testInvalidPatternReplaceRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{sbrokenRegex, i0, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeReplaceRegexLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{sabc, sabc, sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeReplaceRegexSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputTypeReplaceRegex() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_REPLACE_REGEX.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- JOIN -----------------------------------\n     */\n\n    @Test\n    public void testStringJoin() throws EvaluationException {\n        IValue res1 = Operators.STRING_JOIN.evaluate(new IVariable[]{scomma, labc});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"join(',', ['a', 'b', 'c']) = 'a,b,c'\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"a,b,c\"));\n    }\n\n    @Test\n    public void testInvalidInputListType() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_JOIN.evaluate(new IVariable[]{scomma, lint}));\n    }\n\n    @Test\n    public void testInvalidInputListTypeInner() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_JOIN.evaluate(new IVariable[]{scomma, lstringinvalidtypes}));\n    }\n\n    @Test\n    public void testInvalidInputListInfinite() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_JOIN.evaluate(new IVariable[]{scomma, lstring_inf}));\n    }\n\n    @Test\n    public void testInvalidInputSizeJoinLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_JOIN.evaluate(new IVariable[]{scomma, labc, labc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeJoinSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_JOIN.evaluate(new IVariable[]{scomma}));\n    }\n\n    @Test\n    public void testInvalidInputTypeJoin() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_JOIN.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n\n\n    /**\n     * ----------------------------------- NAMED_NAME -----------------------------------\n     */\n\n    @Test\n    public void testStringNamedName() throws EvaluationException {\n        IValue res1 = Operators.NAMED_NAME.evaluate(new IVariable[]{i10});\n        assertThat(\"result is a string\", res1, instanceOf(ValueTypeString.ValueString.class));\n        assertThat(\"name(10) = 10\", ((ValueTypeString.ValueString) res1).getRawValue(), is(\"10\"));\n\n        IValue res2 = Operators.NAMED_NAME.evaluate(new IVariable[]{d10_5});\n        assertThat(\"name(10.5) = 10.5\", ((ValueTypeString.ValueString) res2).getRawValue(), is(\"10.5\"));\n    }\n\n    @Test\n    public void testInvalidInputSizNamedNameLarge() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NAMED_NAME.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeNamedNameSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NAMED_NAME.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeNamedName() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.NAMED_NAME.evaluate(new IVariable[]{DUMMY_VARIABLE}));\n    }\n\n    /**\n     * ----------------------------------- STRING_ERROR -----------------------------------\n     */\n\n    @Test\n    public void testStringErrorWithSymbols() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ERROR.evaluate(sregex));\n    }\n\n    @Test\n    public void testStringErrorWithSpaces() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ERROR.evaluate(shelloWorld));\n    }\n\n    @Test\n    public void testInvalidInputSizeErrorLong() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ERROR.evaluate(new IVariable[]{sabc, sabc}));\n    }\n\n    @Test\n    public void testInvalidInputSizeErrorSmall() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ERROR.evaluate(new IVariable[]{}));\n    }\n\n    @Test\n    public void testInvalidInputTypeError() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> Operators.STRING_ERROR.evaluate(new IVariable[]{DUMMY_VARIABLE, DUMMY_VARIABLE}));\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestValueTypeListProxyFactories.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport com.google.common.collect.Lists;\nimport net.minecraft.nbt.ByteArrayTag;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.IntArrayTag;\nimport net.minecraft.nbt.LongArrayTag;\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxyFactoryTypeRegistry;\nimport org.cyclops.integrateddynamics.core.evaluate.operator.Operators;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Optional;\n\nimport static org.cyclops.integrateddynamics.core.test.TestHelpers.deserialize;\nimport static org.cyclops.integrateddynamics.core.test.TestHelpers.serialize;\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the factory types of value list proxies.\n * Entity and inventory types should be checked manually.\n * @author rubensworks\n */\npublic class TestValueTypeListProxyFactories {\n\n    static { CyclopsCoreInstance.MOD = new ModBaseMocked(); }\n\n    @BeforeAll\n    public static void before() {\n        ValueTypeListProxyFactories.load();\n    }\n\n    @Test\n    public void testAppend() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyAppend(\n                ValueTypeList.ValueList.ofAll(ValueTypeBoolean.ValueBoolean.of(true)).getRawValue(),\n                ValueTypeBoolean.ValueBoolean.of(true)\n        ));\n    }\n\n    @Test\n    public void testConcat() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyConcat<>(\n                ValueTypeList.ValueList.ofAll(ValueTypeBoolean.ValueBoolean.of(true)).getRawValue(),\n                ValueTypeList.ValueList.ofAll(ValueTypeBoolean.ValueBoolean.of(true)).getRawValue()\n        ));\n    }\n\n    @Test\n    public void testLazyBuilt() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyLazyBuilt<>(\n                ValueTypeInteger.ValueInteger.of(0),\n                Operators.ARITHMETIC_ADDITION\n        ));\n    }\n\n    @Test\n    public void testMaterialized() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyMaterialized<>(\n                ValueTypes.BOOLEAN,\n                Lists.newArrayList(ValueTypeBoolean.ValueBoolean.of(true))\n        ));\n    }\n\n    @Test\n    public void testMaterializedIntegerListFromIntArrayTag() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        // Simulate codec round-trip: ListTag<IntTag> -> IntArrayTag (as done by Minecraft's NbtOps)\n        ValueTypeListProxyMaterialized<?, ?> proxy = new ValueTypeListProxyMaterialized<>(\n                ValueTypes.INTEGER,\n                Lists.newArrayList(ValueTypeInteger.ValueInteger.of(42))\n        );\n        CompoundTag tag = serialize(o -> {\n            try {\n                ValueTypeListProxyFactories.REGISTRY.serialize(o, proxy);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        tag.put(\"values\", new IntArrayTag(new int[]{42}));\n        IValueTypeListProxy<?, ?> proxyNew = deserialize(tag, valueInput -> {\n            try {\n                return ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        assertThat(proxyNew, equalTo(proxy));\n    }\n\n    @Test\n    public void testMaterializedLongListFromLongArrayTag() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        // Simulate codec round-trip: ListTag<LongTag> -> LongArrayTag (as done by Minecraft's NbtOps)\n        ValueTypeListProxyMaterialized<?, ?> proxy = new ValueTypeListProxyMaterialized<>(\n                ValueTypes.LONG,\n                Lists.newArrayList(ValueTypeLong.ValueLong.of(123L))\n        );\n        CompoundTag tag = serialize(o -> {\n            try {\n                ValueTypeListProxyFactories.REGISTRY.serialize(o, proxy);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        tag.put(\"values\", new LongArrayTag(new long[]{123L}));\n        IValueTypeListProxy<?, ?> proxyNew = deserialize(tag, valueInput -> {\n            try {\n                return ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        assertThat(proxyNew, equalTo(proxy));\n    }\n\n    @Test\n    public void testMaterializedBooleanListFromByteArrayTag() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        // Simulate codec round-trip: ListTag<ByteTag> -> ByteArrayTag (as done by Minecraft's NbtOps)\n        ValueTypeListProxyMaterialized<?, ?> proxy = new ValueTypeListProxyMaterialized<>(\n                ValueTypes.BOOLEAN,\n                Lists.newArrayList(ValueTypeBoolean.ValueBoolean.of(true))\n        );\n        CompoundTag tag = serialize(o -> {\n            try {\n                ValueTypeListProxyFactories.REGISTRY.serialize(o, proxy);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        tag.put(\"values\", new ByteArrayTag(new byte[]{(byte) 1}));\n        IValueTypeListProxy<?, ?> proxyNew = deserialize(tag, valueInput -> {\n            try {\n                return ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        assertThat(proxyNew, equalTo(proxy));\n    }\n\n    @Test\n    public void testNbtKeys() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyNbtKeys(\n                Optional.of(new CompoundTag())\n        ));\n    }\n\n    @Test\n    public void testNbtValueListByte() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyNbtValueListByte(\n                \"a\",\n                Optional.of(new CompoundTag())\n        ));\n    }\n\n    @Test\n    public void testNbtValueListInt() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyNbtValueListInt(\n                \"a\",\n                Optional.of(new CompoundTag())\n        ));\n    }\n\n    @Test\n    public void testNbtValueListTag() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyNbtValueListTag(\n                \"a\",\n                Optional.of(new CompoundTag())\n        ));\n    }\n\n    @Test\n    public void testNbtValueOperatorMapped() throws IValueTypeListProxyFactoryTypeRegistry.SerializationException {\n        testFactoryType(new ValueTypeListProxyOperatorMapped(\n                Operators.ARITHMETIC_ADDITION,\n                ValueTypeList.ValueList.ofAll(ValueTypeInteger.ValueInteger.of(10)).getRawValue()\n        ));\n    }\n\n    protected void testFactoryType(IValueTypeListProxy<?, ?> proxy) {\n        CompoundTag serialized = serialize(o -> {\n            try {\n                ValueTypeListProxyFactories.REGISTRY.serialize(o, proxy);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        IValueTypeListProxy<?, ?> proxyNew = deserialize(serialized, valueInput -> {\n            try {\n                return ValueTypeListProxyFactories.REGISTRY.deserialize(valueInput);\n            } catch (IValueTypeListProxyFactoryTypeRegistry.SerializationException e) {\n                throw new RuntimeException(e);\n            }\n        }, ValueDeseralizationContextMocked.get().holderLookupProvider());\n        if (!(proxy.isInfinite() && proxy.isInfinite())) {\n            assertThat(proxyNew, equalTo(proxy));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/TestVariables.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.nbt.StringTag;\nimport org.cyclops.cyclopscore.helper.CyclopsCoreInstance;\nimport org.cyclops.integrateddynamics.ModBaseMocked;\nimport org.cyclops.integrateddynamics.api.evaluate.EvaluationException;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\nimport org.hamcrest.CoreMatchers;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Optional;\n\nimport static org.cyclops.integrateddynamics.core.test.TestHelpers.deserialize;\nimport static org.cyclops.integrateddynamics.core.test.TestHelpers.serialize;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.CoreMatchers.not;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\n/**\n * Test the different variable types.\n *\n * @author rubensworks\n */\npublic class TestVariables {\n\n    static {\n        CyclopsCoreInstance.MOD = new ModBaseMocked();\n    }\n\n    public static final ValueDeseralizationContext context = ValueDeseralizationContextMocked.get();\n\n    @Test\n    public void testBooleanType() throws EvaluationException {\n        DummyVariableBoolean bTrue = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(true));\n        assertThat(\"true value is true\", bTrue.getValue().getRawValue(), is(true));\n        assertThat(\"true value is not false\", bTrue.getValue().getRawValue(), not(false));\n\n        DummyVariableBoolean bFalse = new DummyVariableBoolean(ValueTypeBoolean.ValueBoolean.of(false));\n        assertThat(\"false value is false\", bFalse.getValue().getRawValue(), is(false));\n        assertThat(\"false value is not true\", bFalse.getValue().getRawValue(), not(true));\n\n        bTrue.setValue(ValueTypeBoolean.ValueBoolean.of(false));\n        assertThat(\"false value is false\", bTrue.getValue().getRawValue(), is(false));\n        assertThat(\"false value is not true\", bTrue.getValue().getRawValue(), not(true));\n\n        bFalse.setValue(ValueTypeBoolean.ValueBoolean.of(true));\n        assertThat(\"true value is true\", bFalse.getValue().getRawValue(), is(true));\n        assertThat(\"true value is not false\", bFalse.getValue().getRawValue(), not(false));\n\n        bFalse.setValue(ValueTypeBoolean.ValueBoolean.of(false));\n        bTrue.setValue(ValueTypeBoolean.ValueBoolean.of(true));\n        CompoundTag tag0 = new CompoundTag();\n        tag0.putByte(\"v\", (byte) 0);\n        CompoundTag tag1 = new CompoundTag();\n        tag1.putByte(\"v\", (byte) 1);\n\n        assertThat(\"serializing false returns false\", serialize(o -> bFalse.getType().serialize(o, bFalse.getValue()), context.holderLookupProvider()), is(tag0));\n        assertThat(\"serializing true returns true\", serialize(o -> bTrue.getType().serialize(o, bTrue.getValue()), context.holderLookupProvider()), is(tag1));\n\n        assertThat(\"deserializing false returns false\", deserialize(tag0, bFalse.getType()::deserialize, context.holderLookupProvider()), is(bFalse.getValue()));\n        assertThat(\"deserializing true returns true\", deserialize(tag1, bTrue.getType()::deserialize, context.holderLookupProvider()), is(bTrue.getValue()));\n\n        bFalse.setValue(ValueTypeBoolean.ValueBoolean.of(false));\n        bTrue.setValue(ValueTypeBoolean.ValueBoolean.of(true));\n        assertThat(\"serializing false returns false\", bFalse.getType().toString(bFalse.getValue()), is(\"false\"));\n        assertThat(\"serializing true returns true\", bTrue.getType().toString(bTrue.getValue()), is(\"true\"));\n\n        assertThat(\"deserializing false returns false\", bFalse.getType().parseString(\"false\"), is(bFalse.getValue()));\n        assertThat(\"deserializing true returns true\", bTrue.getType().parseString(\"true\"), is(bTrue.getValue()));\n    }\n\n    @Test\n    public void testIntegerType() throws EvaluationException {\n        DummyVariableInteger i0 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(0));\n        assertThat(\"0 value is 0\", i0.getValue().getRawValue(), is(0));\n\n        DummyVariableInteger im10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(-10));\n        assertThat(\"-10 value is -10\", im10.getValue().getRawValue(), is(-10));\n\n        DummyVariableInteger i10 = new DummyVariableInteger(ValueTypeInteger.ValueInteger.of(10));\n        assertThat(\"10 value is 10\", i10.getValue().getRawValue(), is(10));\n\n        CompoundTag tag10 = new CompoundTag();\n        tag10.putInt(\"v\", 10);\n        CompoundTag tagm10 = new CompoundTag();\n        tagm10.putInt(\"v\", -10);\n        CompoundTag tag0 = new CompoundTag();\n        tag0.putInt(\"v\", 0);\n\n        assertThat(\"serializing 10 returns 10\", serialize(o -> i10.getType().serialize(o, i10.getValue()), context.holderLookupProvider()), is(tag10));\n        assertThat(\"serializing -10 returns -10\", serialize(o -> im10.getType().serialize(o, im10.getValue()), context.holderLookupProvider()), is(tagm10));\n        assertThat(\"serializing 0 returns 0\", serialize(o -> i0.getType().serialize(o, i0.getValue()), context.holderLookupProvider()), is(tag0));\n\n        assertThat(\"deserializing 10 returns 10\", deserialize(tag10, i10.getType()::deserialize, context.holderLookupProvider()), is(i10.getValue()));\n        assertThat(\"deserializing -10 returns -10\", deserialize(tagm10, im10.getType()::deserialize, context.holderLookupProvider()), is(im10.getValue()));\n        assertThat(\"deserializing 0 returns 0\", deserialize(tag0, i0.getType()::deserialize, context.holderLookupProvider()), is(i0.getValue()));\n\n        assertThat(\"serializing 10 returns 10\", i10.getType().toString(i10.getValue()), is(\"10\"));\n        assertThat(\"serializing -10 returns -10\", im10.getType().toString(im10.getValue()), is(\"-10\"));\n        assertThat(\"serializing 0 returns 0\", i0.getType().toString(i0.getValue()), is(\"0\"));\n\n        assertThat(\"deserializing 10 returns 10\", i10.getType().parseString(\"10\"), is(i10.getValue()));\n        assertThat(\"deserializing -10 returns -10\", im10.getType().parseString(\"-10\"), is(im10.getValue()));\n        assertThat(\"deserializing 0 returns 0\", i0.getType().parseString(\"0\"), is(i0.getValue()));\n    }\n\n    @Test\n    public void testDoubleType() throws EvaluationException {\n        DummyVariableDouble d0 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(0.1));\n        assertThat(\"0.1 value is 0.1\", d0.getValue().getRawValue(), is(0.1));\n\n        DummyVariableDouble dm10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(-10.1));\n        assertThat(\"-10.1 value is -10.1\", dm10.getValue().getRawValue(), is(-10.1));\n\n        DummyVariableDouble d10 = new DummyVariableDouble(ValueTypeDouble.ValueDouble.of(10.1));\n        assertThat(\"10.1 value is 10.1\", d10.getValue().getRawValue(), is(10.1));\n\n        CompoundTag tag10 = new CompoundTag();\n        tag10.putDouble(\"v\", 10.1);\n        CompoundTag tagm10 = new CompoundTag();\n        tagm10.putDouble(\"v\", -10.1);\n        CompoundTag tag0 = new CompoundTag();\n        tag0.putDouble(\"v\", 0.1);\n\n        assertThat(\"serializing 10.1 returns 10.1\", serialize(o -> d10.getType().serialize(o, d10.getValue()), context.holderLookupProvider()), is(tag10));\n        assertThat(\"serializing -10.1 returns -10.1\", serialize(o -> dm10.getType().serialize(o, dm10.getValue()), context.holderLookupProvider()), is(tagm10));\n        assertThat(\"serializing 0.1 returns 0.1\", serialize(o -> d0.getType().serialize(o, d0.getValue()), context.holderLookupProvider()), is(tag0));\n\n        assertThat(\"deserializing 10.1 returns 10.1\", deserialize(tag10, d10.getType()::deserialize, context.holderLookupProvider()), is(d10.getValue()));\n        assertThat(\"deserializing -10.1 returns -10.1\", deserialize(tagm10, dm10.getType()::deserialize, context.holderLookupProvider()), is(dm10.getValue()));\n        assertThat(\"deserializing 0.1 returns 0.1\", deserialize(tag0, d0.getType()::deserialize, context.holderLookupProvider()), is(d0.getValue()));\n\n        assertThat(\"serializing 10.1 returns 10.1\", d10.getType().toString(d10.getValue()), is(\"10.1\"));\n        assertThat(\"serializing -10.1 returns -10.1\", dm10.getType().toString(dm10.getValue()), is(\"-10.1\"));\n        assertThat(\"serializing 0.1 returns 0.1\", d0.getType().toString(d0.getValue()), is(\"0.1\"));\n\n        assertThat(\"deserializing 10.1 returns 10.1\", d10.getType().parseString(\"10.1\"), is(d10.getValue()));\n        assertThat(\"deserializing -10.1 returns -10.1\", dm10.getType().parseString(\"-10.1\"), is(dm10.getValue()));\n        assertThat(\"deserializing 0.1 returns 0.1\", d0.getType().parseString(\"0.1\"), is(d0.getValue()));\n    }\n\n    @Test\n    public void testStringType() throws EvaluationException {\n        DummyVariableString s0 = new DummyVariableString(ValueTypeString.ValueString.of(\"0\"));\n        assertThat(\"0 value is 0\", s0.getValue().getRawValue(), is(\"0\"));\n\n        DummyVariableString sm10 = new DummyVariableString(ValueTypeString.ValueString.of(\"-10\"));\n        assertThat(\"-10 value is -10\", sm10.getValue().getRawValue(), is(\"-10\"));\n\n        DummyVariableString s10 = new DummyVariableString(ValueTypeString.ValueString.of(\"10\"));\n        assertThat(\"10 value is 10\", s10.getValue().getRawValue(), is(\"10\"));\n\n        CompoundTag tag10 = new CompoundTag();\n        tag10.putString(\"v\", \"10\");\n        CompoundTag tagm10 = new CompoundTag();\n        tagm10.putString(\"v\", \"-10\");\n        CompoundTag tag0 = new CompoundTag();\n        tag0.putString(\"v\", \"0\");\n\n        assertThat(\"serializing 10 returns 10\", serialize(o -> s10.getType().serialize(o, s10.getValue()), context.holderLookupProvider()), is(tag10));\n        assertThat(\"serializing -10 returns -10\", serialize(o -> sm10.getType().serialize(o, sm10.getValue()), context.holderLookupProvider()), is(tagm10));\n        assertThat(\"serializing 0 returns 0\", serialize(o -> s0.getType().serialize(o, s0.getValue()), context.holderLookupProvider()), is(tag0));\n\n        assertThat(\"deserializing 10 returns 10\", deserialize(tag10, s10.getType()::deserialize, context.holderLookupProvider()), is(s10.getValue()));\n        assertThat(\"deserializing -10 returns -10\", deserialize(tagm10, sm10.getType()::deserialize, context.holderLookupProvider()), is(sm10.getValue()));\n        assertThat(\"deserializing 0 returns 0\", deserialize(tag0, s0.getType()::deserialize, context.holderLookupProvider()), is(s0.getValue()));\n\n        assertThat(\"serializing 10 returns 10\", s10.getType().toString(s10.getValue()), is(\"10\"));\n        assertThat(\"serializing -10 returns -10\", sm10.getType().toString(sm10.getValue()), is(\"-10\"));\n        assertThat(\"serializing 0 returns 0\", s0.getType().toString(s0.getValue()), is(\"0\"));\n\n        assertThat(\"deserializing 10 returns 10\", s10.getType().parseString(\"10\"), is(s10.getValue()));\n        assertThat(\"deserializing -10 returns -10\", sm10.getType().parseString(\"-10\"), is(sm10.getValue()));\n        assertThat(\"deserializing 0 returns 0\", s0.getType().parseString(\"0\"), is(s0.getValue()));\n    }\n\n    @Test\n    public void testListTypeMaterialized() throws EvaluationException {\n        ValueTypeListProxyFactories.load();\n\n        DummyVariableList l0 = new DummyVariableList(ValueTypeList.ValueList.ofAll());\n        assertThat(\"empty boolean list has length zero\", l0.getValue().getRawValue().getLength(), is(0));\n        assertThat(\"empty boolean list has boolean type\", l0.getValue().getRawValue().getValueType(), CoreMatchers.<IValueType>is(ValueTypes.CATEGORY_ANY));\n\n        DummyVariableList l2 = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeString.ValueString.of(\"a\"), ValueTypeString.ValueString.of(\"b\"))\n        );\n        assertThat(\"string list has length two\", l2.getValue().getRawValue().getLength(), is(2));\n        assertThat(\"string list has string type\", l2.getValue().getRawValue().getValueType(), CoreMatchers.<IValueType>is(ValueTypes.STRING));\n\n        DummyVariableList l2_2 = new DummyVariableList(ValueTypeList.ValueList.ofAll(\n                ValueTypeList.ValueList.ofAll(ValueTypeString.ValueString.of(\"a\"), ValueTypeString.ValueString.of(\"b\")),\n                ValueTypeList.ValueList.ofAll(ValueTypeString.ValueString.of(\"c\"), ValueTypeString.ValueString.of(\"d\"))\n        ));\n        assertThat(\"nested list has length two\", l2_2.getValue().getRawValue().getLength(), is(2));\n        assertThat(\"nestedlist has list type\", l2_2.getValue().getRawValue().getValueType(), CoreMatchers.<IValueType>is(ValueTypes.LIST));\n\n        DummyVariableList l2h = new DummyVariableList(ValueTypeList.ValueList.ofAll(ValueTypes.CATEGORY_ANY,\n                ValueTypeInteger.ValueInteger.of(42),\n                ValueTypeString.ValueString.of(\"hello\")\n        ));\n        assertThat(\"heterogeneous list has length two\", l2h.getValue().getRawValue().getLength(), is(2));\n        assertThat(\"heterogeneous list has any type\", l2h.getValue().getRawValue().getValueType(), CoreMatchers.<IValueType>is(ValueTypes.CATEGORY_ANY));\n\n        // Make empty list\n        CompoundTag tagEmptySerialized = new CompoundTag();\n        tagEmptySerialized.putString(\"valueType\", \"integrateddynamics:any\");\n        tagEmptySerialized.put(\"values\", new ListTag());\n        CompoundTag tagEmpty = new CompoundTag();\n        tagEmpty.putString(\"proxyName\", \"integrateddynamics:materialized\");\n        tagEmpty.put(\"serialized\", tagEmptySerialized);\n\n        // Make string list\n        CompoundTag tagStringSerialized = new CompoundTag();\n        tagStringSerialized.putString(\"valueType\", \"integrateddynamics:string\");\n        ListTag listString = new ListTag();\n        CompoundTag listString1 = new CompoundTag();\n        listString1.putString(\"v\", \"a\");\n        CompoundTag listString2 = new CompoundTag();\n        listString2.putString(\"v\", \"b\");\n        listString.add(listString1);\n        listString.add(listString2);\n        tagStringSerialized.put(\"values\", listString);\n        CompoundTag tagString = new CompoundTag();\n        tagString.putString(\"proxyName\", \"integrateddynamics:materialized\");\n        tagString.put(\"serialized\", tagStringSerialized);\n\n        // Make nested list\n        CompoundTag tagStringNestedSerialized = new CompoundTag();\n        tagStringNestedSerialized.putString(\"valueType\", \"integrateddynamics:list\");\n        // --> 1\n        CompoundTag tagStringNestedSub1Serialized = new CompoundTag();\n        tagStringNestedSub1Serialized.putString(\"valueType\", \"integrateddynamics:string\");\n        ListTag listStringNestedSub1 = new ListTag();\n        listStringNestedSub1.add(listString1);\n        listStringNestedSub1.add(listString2);\n        tagStringNestedSub1Serialized.put(\"values\", listStringNestedSub1);\n        CompoundTag tagStringNestedSub1 = new CompoundTag();\n        tagStringNestedSub1.putString(\"proxyName\", \"integrateddynamics:materialized\");\n        tagStringNestedSub1.put(\"serialized\", tagStringNestedSub1Serialized);\n        // --> 2\n        CompoundTag tagStringNestedSub2Serialized = new CompoundTag();\n        tagStringNestedSub2Serialized.putString(\"valueType\", \"integrateddynamics:string\");\n        ListTag listStringNestedSub2 = new ListTag();\n        CompoundTag listString3 = new CompoundTag();\n        listString3.putString(\"v\", \"c\");\n        CompoundTag listString4 = new CompoundTag();\n        listString4.putString(\"v\", \"d\");\n        listStringNestedSub2.add(listString3);\n        listStringNestedSub2.add(listString4);\n        tagStringNestedSub2Serialized.put(\"values\", listStringNestedSub2);\n        CompoundTag tagStringNestedSub2 = new CompoundTag();\n        tagStringNestedSub2.putString(\"proxyName\", \"integrateddynamics:materialized\");\n        tagStringNestedSub2.put(\"serialized\", tagStringNestedSub2Serialized);\n        // <--\n        ListTag tagStringNestedSerializedArray = new ListTag();\n        tagStringNestedSerializedArray.add(tagStringNestedSub1);\n        tagStringNestedSerializedArray.add(tagStringNestedSub2);\n        tagStringNestedSerialized.put(\"values\", tagStringNestedSerializedArray);\n        CompoundTag tagStringNested = new CompoundTag();\n        tagStringNested.putString(\"proxyName\", \"integrateddynamics:materialized\");\n        tagStringNested.put(\"serialized\", tagStringNestedSerialized);\n\n        // Make heterogeneous list\n        CompoundTag tagHeterogeneousSerialized = new CompoundTag();\n        tagHeterogeneousSerialized.putString(\"valueType\", \"integrateddynamics:any\");\n        ListTag listHeterogeneous = new ListTag();\n        CompoundTag valueHeterogeneous1 = new CompoundTag();\n        CompoundTag valueHeterogeneous2 = new CompoundTag();\n        valueHeterogeneous1.putString(\"valueType\", \"integrateddynamics:integer\");\n        valueHeterogeneous1.putInt(\"v\", 42);\n        valueHeterogeneous2.putString(\"valueType\", \"integrateddynamics:string\");\n        valueHeterogeneous2.putString(\"v\", \"hello\");\n        listHeterogeneous.add(valueHeterogeneous1);\n        listHeterogeneous.add(valueHeterogeneous2);\n        tagHeterogeneousSerialized.put(\"values\", listHeterogeneous);\n        CompoundTag tagHeterogeneous = new CompoundTag();\n        tagHeterogeneous.putString(\"proxyName\", \"integrateddynamics:materialized\");\n        tagHeterogeneous.put(\"serialized\", tagHeterogeneousSerialized);\n\n        assertThat(\"serializing empty list\",\n                serialize(o -> l0.getType().serialize(o, l0.getValue()), context.holderLookupProvider()), is(tagEmpty));\n        assertThat(\"serializing string list\",\n                serialize(o -> l2.getType().serialize(o, l2.getValue()), context.holderLookupProvider()), is(tagString));\n        assertThat(\"serializing nested list\",\n                serialize(o -> l2.getType().serialize(o, l2_2.getValue()), context.holderLookupProvider()), is(tagStringNested));\n        assertThat(\"serializing heterogeneous list\",\n                serialize(o -> l2h.getType().serialize(o, l2h.getValue()), context.holderLookupProvider()), is(tagHeterogeneous));\n\n        assertThat(\"deserializing empty list\",\n                deserialize(tagEmpty, l0.getType()::deserialize, context.holderLookupProvider()), is(l0.getValue()));\n        assertThat(\"deserializing string list\",\n                deserialize(tagString, l2.getType()::deserialize, context.holderLookupProvider()), is(l2.getValue()));\n        assertThat(\"deserializing nested list\",\n                deserialize(tagStringNested, l2_2.getType()::deserialize, context.holderLookupProvider()), is(l2_2.getValue()));\n        assertThat(\"deserializing heterogeneous list\",\n                deserialize(tagHeterogeneous, l2h.getType()::deserialize, context.holderLookupProvider()), is(l2h.getValue()));\n    }\n\n    @Test\n    public void testNbtType() throws EvaluationException {\n        DummyVariableNbt snull = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of());\n        assertThat(\"null value is empty NBT tag\", snull.getValue().getRawValue(), is(Optional.empty()));\n\n        CompoundTag tag1 = new CompoundTag();\n        tag1.putBoolean(\"abc\", true);\n        CompoundTag tag2 = new CompoundTag();\n        tag2.putBoolean(\"abc\", true);\n\n        StringTag strTag1 = StringTag.valueOf(\"abc\");\n        StringTag strTag2 = StringTag.valueOf(\"abc\");\n\n        CompoundTag tagWrapped = new CompoundTag();\n        tagWrapped.put(\"v\", tag1);\n\n        CompoundTag strTagWrapped = new CompoundTag();\n        strTagWrapped.put(\"v\", strTag2);\n\n        DummyVariableNbt tagVariable = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(tag1));\n        DummyVariableNbt strTagVariable = new DummyVariableNbt(ValueTypeNbt.ValueNbt.of(strTag1));\n\n        assertThat(\"tag value is tag\", tagVariable.getValue().getRawValue().get(), is(tag2));\n        assertThat(\"string tag value is tag\", strTagVariable.getValue().getRawValue().get(), is(strTag2));\n\n        assertThat(\"serializing null value returns empty NBT tag\", serialize(o -> snull.getType().serialize(o, snull.getValue()), context.holderLookupProvider()), is(new CompoundTag()));\n        assertThat(\"serializing tag returns tag\", serialize(o -> tagVariable.getType().serialize(o, tagVariable.getValue()), context.holderLookupProvider()), is(tagWrapped));\n        assertThat(\"serializing string tag returns tag\", serialize(o -> tagVariable.getType().serialize(o, strTagVariable.getValue()), context.holderLookupProvider()), is(strTagWrapped));\n\n        assertThat(\"deserializing null value returns empty NBT tag\", deserialize(new CompoundTag(), snull.getType()::deserialize, context.holderLookupProvider()), is(snull.getValue()));\n        assertThat(\"deserializing tag returns tag\", deserialize(tagWrapped, tagVariable.getType()::deserialize, context.holderLookupProvider()), is(tagVariable.getValue()));\n        assertThat(\"deserializing string tag returns tag\", deserialize(strTagWrapped, strTagVariable.getType()::deserialize, context.holderLookupProvider()), is(strTagVariable.getValue()));\n\n        assertThat(\"serializing null value returns empty NBT tag\", snull.getType().toString(snull.getValue()), is(\"\"));\n        assertThat(\"serializing tag returns tag\", tagVariable.getType().toString(tagVariable.getValue()), is(\"{abc:1b}\"));\n        assertThat(\"serializing string tag returns tag\", strTagVariable.getType().toString(strTagVariable.getValue()), is(\"\\\"abc\\\"\"));\n\n        assertThat(\"deserializing null value returns empty NBT tag\", snull.getType().parseString(\"\"), is(snull.getValue()));\n        assertThat(\"deserializing tag returns tag\", tagVariable.getType().parseString(\"{abc:1b}\"), is(tagVariable.getValue()));\n        assertThat(\"deserializing string tag returns tag\", strTagVariable.getType().parseString(\"\\\"abc\\\"\"), is(strTagVariable.getValue()));\n    }\n\n    @Test\n    public void testNbtTypeInvalidString() throws EvaluationException {\n        Assertions.assertThrows(EvaluationException.class, () -> ValueTypes.NBT.parseString(\"\\\"\"));\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/org/cyclops/integrateddynamics/core/evaluate/variable/ValueDeseralizationContextMocked.java",
    "content": "package org.cyclops.integrateddynamics.core.evaluate.variable;\n\nimport net.minecraft.core.HolderLookup;\nimport net.minecraft.core.Registry;\nimport net.minecraft.resources.ResourceKey;\nimport org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;\n\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\n/**\n * @author rubensworks\n */\npublic class ValueDeseralizationContextMocked {\n\n    public static ValueDeseralizationContext get() {\n        return new ValueDeseralizationContext(new HolderLookup.Provider() {\n            @Override\n            public Stream<ResourceKey<? extends Registry<?>>> listRegistryKeys() {\n                return Stream.empty();\n            }\n\n            @Override\n            public <T> Optional<HolderLookup.RegistryLookup<T>> lookup(ResourceKey<? extends Registry<? extends T>> p_256285_) {\n                return Optional.empty();\n            }\n        });\n    }\n}\n"
  }
]